

/**************************************************************
 BEGIN site/base                                           */
//<script>


window.addEvent('domready', function () {
	MochaUI.Modal = new MochaUI.Modal();
	// This runs when a person leaves your page.
	window.addEvent('unload', function(){
		if (MochaUI) MochaUI.garbageCleanUp();
	});
});

var jsoRegisterTemplate = new Class({
	initialize: function () {
		this.registry = new Hash();
	},
	registerComponent: function (name, reference) {
		var nr = new Hash();
		nr.name = name;
		nr.reference = reference;
		nr.uid = nr.uid;
		this.registry.include(nr.uid, nr);
	},
	dumpRegister: function () {
		console.log('++ JSO REGISTRY ++');
		$each(this.registry, function (v) {
			console.log('---------------- '+v.name+': '+v.reference.uid);
			console.log(v.reference);
			console.log('---------------- END '+v.name+': '+v.reference.uid);
			console.log(' ');
		}, this);
	}
});
var jsoRegister = false;
window.addEvent('domready', function () {
	jsoRegister = new jsoRegisterTemplate();
});





if (typeof console == 'undefined' || typeof console.log == 'undefined') { console = { log : function (text) { /* alert (text); */ return false; } } }
function rolldoc (resp) {
	if (resp.err)
		return show_error(resp.msg);
	document.location = document.location;
}

function nothing () {
	return;
}

function sprintf ( ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Ash Searle (http://hexmen.com/blog/)
    // + namespaced by: Michael White (http://getsprink.com)
    // +    tweaked by: Jack
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: Paulo Freitas
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // *     example 1: sprintf("%01.2f", 123.1);
    // *     returns 1: 123.10
    // *     example 2: sprintf("[%10s]", 'monkey');
    // *     returns 2: '[    monkey]'
    // *     example 3: sprintf("[%'#10s]", 'monkey');
    // *     returns 3: '[####monkey]'

    var regex = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEG])/g;
    var a = arguments, i = 0, format = a[i++];

    // pad()
    var pad = function (str, len, chr, leftJustify) {
        if (!chr) {chr = ' ';}
        var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
        return leftJustify ? str + padding : padding + str;
    };

    // justify()
    var justify = function (value, prefix, leftJustify, minWidth, zeroPad, customPadChar) {
        var diff = minWidth - value.length;
        if (diff > 0) {
            if (leftJustify || !zeroPad) {
                value = pad(value, minWidth, customPadChar, leftJustify);
            } else {
                value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
            }
        }
        return value;
    };

    // formatBaseX()
    var formatBaseX = function (value, base, prefix, leftJustify, minWidth, precision, zeroPad) {
        // Note: casts negative numbers to positive ones
        var number = value >>> 0;
        prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
        value = prefix + pad(number.toString(base), precision || 0, '0', false);
        return justify(value, prefix, leftJustify, minWidth, zeroPad);
    };

    // formatString()
    var formatString = function (value, leftJustify, minWidth, precision, zeroPad, customPadChar) {
        if (precision != null) {
            value = value.slice(0, precision);
        }
        return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar);
    };

    // doFormat()
    var doFormat = function (substring, valueIndex, flags, minWidth, _, precision, type) {
        var number;
        var prefix;
        var method;
        var textTransform;
        var value;

        if (substring == '%%') {return '%';}

        // parse flags
        var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, customPadChar = ' ';
        var flagsl = flags.length;
        for (var j = 0; flags && j < flagsl; j++) {
            switch (flags.charAt(j)) {
                case ' ': positivePrefix = ' '; break;
                case '+': positivePrefix = '+'; break;
                case '-': leftJustify = true; break;
                case "'": customPadChar = flags.charAt(j+1); break;
                case '0': zeroPad = true; break;
                case '#': prefixBaseX = true; break;
            }
        }

        // parameters may be null, undefined, empty-string or real valued
        // we want to ignore null, undefined and empty-string values
        if (!minWidth) {
            minWidth = 0;
        } else if (minWidth == '*') {
            minWidth = +a[i++];
        } else if (minWidth.charAt(0) == '*') {
            minWidth = +a[minWidth.slice(1, -1)];
        } else {
            minWidth = +minWidth;
        }

        // Note: undocumented perl feature:
        if (minWidth < 0) {
            minWidth = -minWidth;
            leftJustify = true;
        }

        if (!isFinite(minWidth)) {
            throw new Error('sprintf: (minimum-)width must be finite');
        }

        if (!precision) {
            precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : undefined;
        } else if (precision == '*') {
            precision = +a[i++];
        } else if (precision.charAt(0) == '*') {
            precision = +a[precision.slice(1, -1)];
        } else {
            precision = +precision;
        }

        // grab value using valueIndex if required?
        value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];

        switch (type) {
            case 's': return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar);
            case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
            case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
            case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'i':
            case 'd':
                number = parseInt(+value, 10);
                prefix = number < 0 ? '-' : positivePrefix;
                value = prefix + pad(String(Math.abs(number)), precision, '0', false);
                return justify(value, prefix, leftJustify, minWidth, zeroPad);
            case 'e':
            case 'E':
            case 'f':
            case 'F':
            case 'g':
            case 'G':
                number = +value;
                prefix = number < 0 ? '-' : positivePrefix;
                method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
                textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
                value = prefix + Math.abs(number)[method](precision);
                return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
            default: return substring;
        }
    };

    return format.replace(regex, doFormat);
}


function getNamedElements(parent) {
	var eles = $(parent).getElements('*');
	var found_eles = [];
	$each(eles, function (e) {
		if (typeof e.get == 'function' && e.get('name') != null && e.get('name').length > 0)
			found_eles.include(e);
	});
	return found_eles;
}
function getElementNamed(parent, name) {
	var eles = $(parent).getElements('*');
	var found_ele = false;
	$each(eles, function (e) {
		if (typeof e.get == 'function' && e.get('name') == name && found_ele == false)
			found_ele = e;
	});
	return found_ele;
}

// nasty but effective
function time_string_to_hours(time_string) {
	var hours = 0;
	var minutes = 0;
	var t = time_string.split(' hours');
	if (t.length > 1)
		hours = t[0];

	var t = time_string.split(' minutes');
	if (t.length > 1) {
		t = t[0].replace('hours', '|');
		t = t.replace(',', '');
		t = t.replace(' ', '');
		t = t.split('|');
		if (t.length == 1)
			minutes = t[0];
		else if (t.length == 2)
			minutes = t[1];
	}
	return parseFloat(hours) + parseFloat(minutes / 60);
}
function time_string_to_minutes(time_string) {
	var hours = 0;
	var minutes = 0;
	var t = time_string.split(' hours');
	if (t.length > 1) {
		hours = t[0];
	}
	var t = time_string.split(' minutes');
	if (t.length > 1) {
		t = t[0].replace('hours', '|');
		t = t.replace(',', '');
		t = t.replace(' ', '');
		t = t.split('|');
		if (t.length == 1)
			minutes = t[0];
		else if (t.length == 2)
			minutes = t[1];
	}
	return (parseFloat(hours) * 60) + parseFloat(minutes);
}
function minutes_to_time_string(mins) {
	var hours = 0;
	var minutes = 0;
	
	hours = Math.floor(parseFloat(mins) / 60);
	minutes = parseFloat(mins) - (hours * 60);

	var outstring = '';
	if (hours > 0)
		outstring = hours + ' hours';
	
	if (hours > 0 && minutes > 0)
		outstring += ', ';
	
	if (minutes > 0)
		outstring += minutes + ' minutes';
	return outstring;
}
function hours_to_time_string(hrs) {
	return minutes_to_time_string(parseFloat(hrs) * 60);
}











function discard(ele) {
	if ($(ele))
		$(ele).dispose();
	return true;
}


function cloneObj(o) {
     if(typeof(o) != 'object') return o;
     if(o == null) return o;
   
     var newO = new Object();
   
     for(var i in o) newO[i] = cloneObj(o[i]);
      return newO;
}

function queryAndReplace(query, replace_el_id) {
	var myAjax = new Request({
		url:URL+query,
		method: 'post',
		onComplete: function(text) {
			$(replace_el_id).set('html', text);
		}
	});
	myAjax.send();
	return;
} 


// This function removes non-numeric characters
function stripNonNumeric( str ) {
   	str += '';
   	var rgx = /^\d|\.|-$/;
   	var out = '';
	for( var i = 0; i < str.length; i++ ) {
		if( rgx.test( str.charAt(i) ) ){
			if( 
				!( ( str.charAt(i) == '.' && out.indexOf( '.' ) != -1 ) ||
				( str.charAt(i) == '-' && out.length != 0 ) ) 
			){
				out += str.charAt(i);
			}
		}
  	}
	return out;
}

function number_format(format_string) {
   if (! isType(format, 'string')) {return "";} // sanity check
  
   var hasComma = -1 < format.indexOf(','),
     psplit = format.stripNonNumeric().split('.'),
     that = this;
  
   // compute precision
   if (1 < psplit.length) {
     // fix number precision
     that = that.toFixed(psplit[1].length);
   }
   // error: too many periods
   else if (2 < psplit.length) {
     throw('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
   }
   // remove precision
   else {
     that = that.toFixed(0);
   } 
  
   // get the string now that precision is correct
   var fnum = that.toString();
  
   // format has comma, then compute commas
   if (hasComma) {
     // remove precision for computation
     psplit = fnum.split('.');
  
     var cnum = psplit[0],
       parr = [],
       j = cnum.length,
       m = Math.floor(j / 3),
       n = cnum.length % 3 || 3; // n cannot be ZERO or causes infinite loop
  
     // break the number into chunks of 3 digits; first chunk may be less than 3
     for (var i = 0; i < j; i += n) {
       if (i != 0) {n = 3;}
       parr[parr.length] = cnum.substr(i, n);
       m -= 1;
     }
  
     // put chunks back together, separated by comma
     fnum = parr.join(',');
  
     // add the precision back in
     if (psplit[1]) {fnum += '.' + psplit[1];}
   } 
  
   // replace the number portion of the format with fnum
   return format.replace(/[\d,?\.?]+/, fnum);
}

function cash_format(amount) {
	if (typeof amount.replace != 'undefined')
		amount = amount.replace('$', '');
	var i = parseFloat(amount);
	if(isNaN(i)) { i = 0.00; }
	var minus = '';
	if(i < 0) { minus = '-'; }
	i = Math.abs(i);
	i = parseInt((i + .005) * 100);
	i = i / 100;
	s = new String(i);
	if(s.indexOf('.') < 0) { s += '.00'; }
	if(s.indexOf('.') == (s.length - 2)) { s += '0'; }
	s = minus + s;
	return s;
}




function popform_callback (response) {
	if (response.err) {
		show_error(response.msg);
		return false;
	}
	
	var properties = {
		'width':'800px'
	}
	Dialog.box(response.data, properties);
	return false;
}
function mailto (email) {
	document.location = 'mailto:'+email;
}

function showPopupWithText(text) {
	Dialog.box(text)
}
var player = '';

//open and start the audio player
//function listenpopUp(url) {
//window.open(url,'','scrollbars,resizable,width=320px,height=120px,left=50px,top=50px,toolbar=no');
//}
function listenpopUp() {
	player=window.open('listen.php','', 'toolbar=0,scrollbars=0,resizable=1,height=220,width=320'); 
	if (!player.opener) player.opener = self;
	
}



function pop_out(pop_url) {
	day = new Date();
	id = day.getTime();
	eval("page" + id + " = window.open(pop_url, '" + id + "', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=380,height=500');");
	return id;
}


function truncate(str, limit) {
	if (typeof str == 'undefined' || typeof limit == 'undefied')
		return '';
	if (str.length < limit)
		return str;
	var bits, i;

	bits = str.split('');
	if (bits.length > limit) {
		for (i = bits.length - 1; i > -1; --i) {
			if (i > limit) {
				bits.length = i;
			}
			else if (' ' === bits[i]) {
				bits.length = i;
				break;
			}
		}
		bits.push('...');
	}
	return bits.join('');
}


function popup(url, width, height, left, top) {
	if (typeof width == 'undefined')
		width = 600;
	if (typeof height == 'undefined')
		height = 450;
	if (typeof url == 'undefined')
		return false;
	if (typeof left == 'undefined')
		left = 200;
	if (typeof top == 'undefined')
		top = 100;
	window.open(url, '', 'menubar=1,location=1,resizable=1,scrollbars=1,dependent=1,status=1,width='+width+',height='+height+',left='+left+',top='+top);
	return false;
}


function moreInfoLink(url){
	try {
		//window.opener.document.title = window.opener.document.title;
		//return "Parent Window is Open!";
		var re = /www.grcmc.org\/radio\//i
		opener_url = window.opener.location;

		if(opener_url){
			//if the user is still in the local resonance structure
			if(re.test(opener_url)){
				//change the opener page location
				//alert('still in lr');
				window.opener.location.replace(url);
			} else {
				//alert('not in lr');
				window.opener.open(url);
			}
			window.opener.focus();
			
		} else {
			alert('out of domain');
		}
	}
	catch(e) {
		window.open(url)
	 }
}

function moreInfoLinkO(url){

	if(!window.opener){
		window.open(url);
	} else {
		var opener_url = ''
		try{
			opener_url = window.opener.location;
		} catch(err) {
			opener_url = '';
		}
		var re = /www.grcmc.org\/radio\//i

		if(opener_url){
			//if the user is still in the local resonance structure
			if(re.test(opener_url)){
				//change the opener page location
				//alert('still in lr');
				window.opener.location.replace(url);
			} else {
				//alert('not in lr');
				window.opener.open(url);
			}
			window.opener.focus();
			
		} else {
			alert('out of domain');
		}
	}
}

function set_expander_events () {
	$each($$('.expander'), function (ele) {
		if (!ele.retrieve('jso_complete')) {
			ele.addEvent('click', function () {
				$$('.expander_tip').fade('out').destroy();
				if ($(this).hasClass('open')) { 
					$(this).addClass('closed').removeClass('open').getNext().hide(); 
					if (this.hasClass('expander_boxhelp'))
						$(this).set('html', 'show form description')
				} else {
					if (!$(this).hasClass('multi_open'))
						$$('.expander').removeClass('open').addClass('closed').getNext().hide();
					$(this).addClass('open').removeClass('closed').getNext().show(); 
					if (this.hasClass('expander_boxhelp'))
						$(this).set('html', 'hide form description')
				}
			});
			ele.store('jso_complete', true);
		}
	});
}

window.addEvent('domready', function () {
	set_expander_events();
	
	var smalls = $$('small[html^=\[tk\]]');
	for (i in smalls) {
		if (smalls[i].set)
			smalls[i].set('html', '<a href="#" class="truncate_link">[more]</a>');
	}
	$$('.truncate_link').addEvent('click', function () {
		var parent = this.getParent();
		if (!parent.getNext('div'))
			parent = parent.getParent();
		if (!parent.getNext('div'))
			return;
		var content = parent.getNext('div').get('html');
		show_info(content);
		return false;	
	});

	$$('.qtips').each(function(element,index) {
		if (element.get('title') !== null) {
			var content = element.get('title').split('::');
			element.store('tip:title', content[0]);
		    element.store('tip:text', content[1]);
	
			var qtips = new Tips(element);
			element.store('tips', qtips);
		}
	});
	

	
});
	
	
function ascii_value (c)
{
	// restrict input to a single character
	c = c . charAt (0);

	// loop through all possible ASCII values
	var i;
	for (i = 0; i < 256; ++ i)
	{
		// convert i into a 2-digit hex string
		var h = i . toString (16);
		if (h . length == 1)
			h = "0" + h;

		// insert a % character into the string
		h = "%" + h;

		// determine the character represented by the escape code
		h = unescape (h);

		// if the characters match, we've found the ASCII value
		if (h == c)
			break;
	}
	return i;
}

function random_string(string_length) {
	var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
	if (typeof string_length == 'undefined')
		string_length = 8;
	var randomstring = '';
	for (var i=0; i<string_length; i++) {
		var rnum = Math.floor(Math.random() * chars.length);
		randomstring += chars.substring(rnum,rnum+1);
	}
	return randomstring;
}



function $_GET(key) {
	var args = new Object();
	var query = location.search.substring(1);
	var pairs = query.split("&");
	for(var i = 0; i < pairs.length; i++) {
		var pos = pairs[i].indexOf('=');
		if (pos == -1) continue;
		var argname = pairs[i].substring(0,pos);
		var value = pairs[i].substring(pos+1);
		args[argname] = unescape(value);
	}

	if (typeof key == 'undefined') 
		return args;
	
	if (typeof args[key] == 'undefined')
		return null;
	else
		return args[key]
} 




/**
 * Accepts 2 arrays, x and y
 * Loops through both, returns a single array containing
 * a merged copy of the two
 */
function mergeArrays( x, y ) {
	var t = [];
	var cnt = 0;
	
	for( var i = 0; i < x.length; ++i ) {
		t[cnt] = x[i];
		cnt++;
	}
	
	for( var i = 0; i < y.length; ++i ) {
		t[cnt] = y[i];
		cnt++;
	}
	
	return t;
}


function stringToElement(string) {
	var ele = new Element('div', {
		'html':string
	});
	return ele;
}



var aj = false;
window.addEvent('domready', function () {
	aj = new ajax_request();
});
var ajax_request = new Class({
	initialize: function () {
		this.req = new Request({
			'link':'chain',
			'noCache':true,
			'method':'post'
		});
	},
	get: function (handler, data, call) {
		if (typeof call == 'string') {
			call += '(json_resp)';
		}
		this.req.send({
			'url':URL+'index.php',
			'onComplete': function(text) { // no default update element, we do it manually
				console.log(call)
				alert('v');
				eval(call);
			},
			'data': 'component='+handler+'&'+data
		});
	}
})


/**
 * Generic ajax call to handler
 */
function req (handler, data, callback) {
	callback += '(text)';
	var url     = 'index.php';
	
	var myAjax = new Request({
		url:url,
		method: 'post',
		onComplete: function(text) { // no default update element, we do it manually
			eval(callback);
		}
	});
	myAjax.send('component='+handler+'&'+data);
	return;		
	
}

/**
 * Generic ajax call, returns json object
 */
function jsonreq (handler, data, call) {
	if (typeof call == 'string') {
		call += '(json_resp)';
	}
	var myAjax = new Request({
		processScripts: false,
		url:CURRENT_URL+'index.php',
		method: 'post',
		onComplete: function(responseText) {
			json_resp = eval('(' + responseText + ')');
			if (typeof call == 'string') {
				eval(call);
			} else if (typeof call == 'function') {
				call(json_resp);
			}
		}
	});
	myAjax.send('component='+handler+'&'+data);
	return;
}

/**
 * Generic ajax call, returns json object
 */
function jsonreqs (handler, data, call) {
	if (typeof call == 'string') {
		call += '(json_resp)';
	}
	var myAjax = new Request({
		url:SITE_URL+'index.php',
		method: 'post',
		onComplete: function(responseText) {

			json_resp = eval('(' + responseText + ')');
			if (typeof call == 'string') {
				eval(call);
			} else if (typeof call == 'function') {
				call(json_resp);
			}
		}
	});
	myAjax.send('component='+handler+'&'+data);
	return;
}

function titleCaps(title) {
        var small = "(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v[.]?|via|vs[.]?)";
        var punct = "([!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]*)";
        var parts = [], split = /[:.;?!] |(?: |^)["Ò]/g, index = 0;
        title = lower(title);
        while (true) {
                var m = split.exec(title);
                parts.push( title.substring(index, m ? m.index : title.length)
                        .replace(/\b([A-Za-z][a-z.'Õ]*)\b/g, function(all){
                                return /[A-Za-z]\.[A-Za-z]/.test(all) ? all : upper(all);
                        })
                        .replace(RegExp("\\b" + small + "\\b", "ig"), lower)
                        .replace(RegExp("^" + punct + small + "\\b", "ig"), function(all, punct, word){
                                return punct + upper(word);
                        })
                        .replace(RegExp("\\b" + small + punct + "$", "ig"), upper));
               
                index = split.lastIndex;
               
                if ( m ) parts.push( m[0] );
                else break;
        }
               
        return parts.join("").replace(/ V(s?)\. /ig, " v$1. ")
                .replace(/(['Õ])S\b/ig, "$1s")
                .replace(/\b(AT&T|Q&A)\b/ig, function(all){
                        return all.toUpperCase();
                });
}
   
function lower(word){
        return word.toLowerCase();
}
function upper(word){
  return word.substr(0,1).toUpperCase() + word.substr(1);
}

function implode( glue, pieces ) {
    // Joins array elements placing glue string between items and return one string  
    // example: implode(' ', ['Apples', 'and', 'Oranges']);
    // returns: 'Apples and Oranges'
    return ( ( pieces instanceof Array ) ? pieces.join ( glue ) : pieces );
}









function urldecode (encoded_text) {
	return decodeURIComponent(encoded_text.replace(/\+/g,  " "));
}






function formatCurrency(num) {
num = num.toString().replace(/\$|\,/g,'');
if(isNaN(num))
num = "0";
sign = (num == (num = Math.abs(num)));
num = Math.floor(num*100+0.50000000001);
cents = num%100;
num = Math.floor(num/100).toString();
if(cents<10)
cents = "0" + cents;
for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
num = num.substring(0,num.length-(4*i+3))+','+
num.substring(num.length-(4*i+3));
return (((sign)?'':'-') + '$' + num + '.' + cents);
}

function IsNumeric(strString)
   //  check for valid numeric strings	
   {
   var strValidChars = "0123456789";
   var strChar;
   var blnResult = true;

	if (typeof strString == 'undefined' || strString == null)
		return false;
		
   if (strString.length == 0) return false;

   //  test strString consists of valid characters listed above
   for (i = 0; i < strString.length && blnResult == true; i++)
      {
      strChar = strString.charAt(i);
      if (strValidChars.indexOf(strChar) == -1)
         {
         blnResult = false;
         }
      }
   return blnResult;
   }


function goto(page) {
	location.href = page;
	return;
}
function redirect(page, query) {
	var loc = SITE_URL+'index.php?page='+page;
	if (typeof query != 'undefined')
		loc += '&'+query;
	document.location.href = loc;
	return;
}

function redirects(page, query) {
	var loc = SITE_URLS+'index.php?page='+page;
	if (typeof query != 'undefined')
		loc += '&'+query;
	document.location.href = loc;
	return;
}









function callback(func,opts){ 
	var cb = function(){ 
		var args = opts.args ? opts.args : [];
		var bind = opts.bind ? opts.bind : this;
		var fargs = opts.supressArgs === true ? [] : toArray(arguments);
		func.apply(bind,fargs.concat(args));
	 } 
	 return cb; 
} 
/* A utility function for callback() */ 
function toArray(arrayLike){ 
	var arr = []; 
	for(var i = 0; i < arrayLike.length; i++) {
		arr.push(arrayLike[i]);
	} 
	return arr; 
}



var componentJSO = new Class({
	Implements: Options,
	options: {},
	uid:'',

	// data for dialogs
	confirmData: false,
	errorData: false,
	questionData: false,
	infoData: false,

	promptData: false,
	promptButtonClicked: false,

	boxCallback: $empty(),
	boxOnClose: $empty(),
	boxContentEl: new Element('div'),
	boxOpen: false,

	loading_message: '<center><h3>Please wait</h3><img src="'+(typeof SITE_URLS != 'undefined' ? SITE_URLS : '')+'site/img/system/small_loading.gif" /></center>',
	showLoadingMessage: function (ele, message) {
		if (!ele || !$chk(ele.set))
			return false;

		if ($chk(message)) {
			ele.set('html', '<center><img src="'+(typeof SITE_URLS != 'undefined' ? SITE_URLS : '')+'site/img/system/small_loading.gif" />'+message+'</center>');
		} else {
			ele.set('html', this.loading_message);
		}
		return;
	},

	$: function (id) {
		var c_name = this.uid+'_container';
		return $(c_name).getElements('.'+id)[0];
	},
	$$: function (arg) {
		var c_name = this.uid+'_container';
		return $(c_name).getElements(arg);
	},
	showResultsInBox: function (resp) {
		return this.show_results_in_box(resp);
	},
	show_results_in_box: function (resp) {
		if (resp.err) {
			return show_error(resp.msg);
		}
		
		if (!this.boxOpen)
			Dialog.box(resp.data, {'onClose':this.closingShowInBox.bind(this)});
		else
			this.boxContentEl.set('html', resp.data);

		setTimeout(this.finalizeShowInBox.bind(this), 250);
		return resp;
	},
	finalizeShowInBox: function () {
		if ($('box_content_div')) {
			this.boxOpen = true;
			this.boxContentEl = $('box_content_div');
		
			set_expander_events();
			$('box_content_div').getElements('*[class*=qtips]').each(function(element,index) {
				if (element.get('title') !== null && !element.hasClass('qtips_initialized')) {
					element.addClass('qtips_initialized');
					var content = element.get('title').split('::');
					element.store('tip:title', content[0]);
				    element.store('tip:text', content[1]);
	
					var qtips = new Tips(element);
					element.store('tips', qtips);
				}
			});
			Form.init_forms();
		} else {
			console.log('After show_results_in_box, could not find box');
		}

		if (typeof this.boxCallback == 'function')
			this.boxCallback();

		this.boxCallback = $empty();
	},
	closingShowInBox: function () {
		this.boxOpen = false;
		if (typeof this.boxOnClose == 'function')
			this.boxOnClose();
		this.boxOnClose = $empty();
	},
	
	



	showConfirm: function (msg, title, cb, cancel_cb, data) {
		if (typeof cb != 'function')
			return;
		if (typeof cancel_cb != 'function')
			cancel_cb = $empty;
		if (typeof data == 'undefined')
			data = false;

		if (typeof msg == 'undefined')
			msg = 'Enter the requested data:';
		if (typeof title == 'undefined')
			title = 'Confirmation';

		cb = cb.bind(this);

		msg = '<div class="dialogContentArea"><strong>'+title+'</strong><br />'+msg+'</div><br /><br />';
		msg += '<div class="dialogButtonArea"><button id="'+this.uid+'_confirm_ok" class="styledButton"><span class="ok">Yes</span></button><button id="'+this.uid+'_confirm_cancel" class="styledButton"><span class="cancel">Cancel</span></button></div><div class="clearBoth"></div>';

		var confirmWindow = new MochaUI.Window({
			id: this.uid+'_confirm_window',
			title: title,
			loadMethod: 'html',
			content: msg,
			width: 350,
			height: 275,
			type: 'modal',
			draggable: true,
			onClose: this.handleConfirmClose.bind(this)
		});
		confirmWindow.contentWrapperEl.setStyles({
			'background-image': 'url(site/img/dialog/warning_bg.jpg)',
			'background-repeat': 'no-repeat',
			'background-position': 'bottom right'
		});
		this.confirmData = {
			'data':data, 
			'cb':cb, 
			'cancel_cb':cancel_cb,
			'okEventBinding': $(this.uid+'_confirm_ok').addEvent('click', this.handleConfirmOk.bind(this)),
			'cancelEventBinding': $(this.uid+'_confirm_cancel').addEvent('click', this.handleConfirmClose.bind(this)),
			'window': confirmWindow,
			'buttonClicked': false
		};
		return;
	},
	handleConfirmOk: function () {
		this.confirmData.buttonClicked = true;
		this.confirmData.cb(this.confirmData.data);
		MochaUI.closeWindow(this.confirmData.window.windowEl);
	},
	handleConfirmClose: function (e) {
		if ($chk(e.hasClass) && e.hasClass('mocha')) {
			// the window is closing... has their been a button yet?
			if (this.confirmData.buttonClicked == false) {
				// no click, this is the same as cancel
				this.confirmData.cancel_cb();
				MochaUI.closeWindow(this.confirmData.window.windowEl);
			} else {
				// yes clicked, this is just a close
				this.cleanConfirm();
			}
			return;
		} else {
			// this is a cancel button click
			this.confirmData.buttonClicked = true;
			this.confirmData.cancel_cb();
			MochaUI.closeWindow(this.confirmData.window.windowEl);
		}
	},
	cleanConfirm: function () {
		$(this.uid+'_confirm_ok').removeEvent('click', this.confirmData.okEventBinding);
		$(this.uid+'_confirm_cancel').removeEvent('click', this.confirmData.cancelEventBinding);
		this.confirmData = false;
	},


	showInfo: function (msg, title, cb, data) {
		if (typeof cb != 'function')
			cb = $empty;
		if (typeof cancel_cb != 'function')
			cancel_cb = $empty;
		if (typeof data == 'undefined')
			data = false;

		if (typeof msg == 'undefined')
			msg = 'Enter the requested data:';
		if (typeof title == 'undefined')
			title = 'Infoation';

		cb = cb.bind(this);

		msg = '<div class="dialogContentArea"><strong>'+title+'</strong><br />'+msg+'</div>';
		msg += '<div class="dialogButtonArea"><button id="'+this.uid+'_info_ok" class="styledButton"><span class="ok">OK</span></button></div><div class="clearBoth"></div>';

		var infoWindow = new MochaUI.Window({
			id: this.uid+'_info_window',
			title: title,
			loadMethod: 'html',
			content: msg,
			width: 350,
			height: 275,
			type: 'modal',
			draggable: true,
			onClose: this.handleInfoClose.bind(this)
		});
		infoWindow.contentWrapperEl.setStyles({
			'background-image': 'url(site/img/dialog/info_bg.jpg)',
			'background-repeat': 'no-repeat',
			'background-position': 'bottom right'
		});
		this.infoData = {
			'data':data, 
			'cb':cb, 
			'okEventBinding': $(this.uid+'_info_ok').addEvent('click', this.handleInfoOk.bind(this)),
			'window': infoWindow,
			'buttonClicked': false
		};
		return;
	},
	handleInfoOk: function () {
		this.infoData.buttonClicked = true;
		this.infoData.cb(this.infoData.data);
		MochaUI.closeWindow(this.infoData.window.windowEl);
	},
	handleInfoClose: function (e) {
		// the window is closing... has their been a button yet?
		if (this.infoData.buttonClicked == false) {
			MochaUI.closeWindow(this.infoData.window.windowEl);
		} else {
			// yes clicked, this is just a close
			this.cleanInfo();
		}
		return;
	},
	cleanInfo: function () {
		$(this.uid+'_info_ok').removeEvent('click', this.infoData.okEventBinding);
		this.infoData = false;
	},




	showQuestion: function (msg, cb, data) {
		if (typeof cb != 'function')
			return;

		cb = cb.bind(this);
		this.questionData = {'data':data, 'cb':cb};
		show_question(msg, 'JSO_'+this.uid+'.showQuestionCallback.bind(JSO_'+this.uid+')');
		return;
	},
	showQuestionCallback: function (answer) {
		if (this.questionData == false)
			return;

		if (!answer)
			return;

		if (typeof this.questionData.cb != 'function')
			return;

		this.questionData.cb(this.questionData.data, answer);
		this.questionData = false;
		return;
	},
	




	showPrompt: function (msg, title, cb, cancel_cb, data) {
		if (typeof cb != 'function')
			return;
		if (typeof cancel_cb != 'function')
			cancel_cb = $empty;
		if (typeof data == 'undefined')
			data = false;

		if (typeof msg == 'undefined')
			msg = 'Enter the requested data:';
		if (typeof title == 'undefined')
			title = 'Information request';

		cb = cb.bind(this);

		msg = '<div class="dialogContentArea"><strong>'+title+'</strong><br />'+msg+'<textarea></textarea></div><br /><br />';
		msg += '<div class="dialogButtonArea"><button id="'+this.uid+'_prompt_ok" class="styledButton"><span class="ok">OK</span></button><button id="'+this.uid+'_prompt_cancel" class="styledButton"><span class="cancel">Cancel</span></button></div><div class="clearBoth"></div>';

		var promptWindow = new MochaUI.Window({
			id: this.uid+'_prompt_window',
			title: title,
			loadMethod: 'html',
			content: msg,
			width: 350,
			height: 275,
			type: 'modal',
			draggable: true,
			onClose: this.handlePromptClose.bind(this)
		});
		promptWindow.contentWrapperEl.setStyles({
			'background-image': 'url(site/img/dialog/prompt_bg.jpg)',
			'background-repeat': 'no-repeat',
			'background-position': 'bottom right'
		});
		this.promptData = {
			'data':data, 
			'cb':cb, 
			'cancel_cb':cancel_cb,
			'okEventBinding': $(this.uid+'_prompt_ok').addEvent('click', this.handlePromptOk.bind(this)),
			'cancelEventBinding': $(this.uid+'_prompt_cancel').addEvent('click', this.handlePromptClose.bind(this)),
			'window': promptWindow,
			'buttonClicked': false
		};

		return;
	},
	handlePromptOk: function () {
		this.promptData.buttonClicked = true;
		var te = this.promptData.window.contentEl.getElement('textarea');
		this.promptData.cb(te.get('value'), this.promptData.data);
		MochaUI.closeWindow(this.promptData.window.windowEl);
	},
	handlePromptClose: function (e) {
		if ($chk(e.hasClass) && e.hasClass('mocha')) {
			// the window is closing... has their been a button yet?
			if (this.promptData.buttonClicked == false) {
				// no click, this is the same as cancel
				this.promptData.cancel_cb();
				MochaUI.closeWindow(this.promptData.window.windowEl);
			} else {
				// yes clicked, this is just a close
				this.cleanPrompt();
			}
			return;
		} else {
			// this is a cancel button click
			this.promptData.buttonClicked = true;
			this.promptData.cancel_cb();
			MochaUI.closeWindow(this.promptData.window.windowEl);
		}
	},
	cleanPrompt: function () {
		$(this.uid+'_prompt_ok').removeEvent('click', this.promptData.okEventBinding);
		$(this.uid+'_prompt_cancel').removeEvent('click', this.promptData.cancelEventBinding);
		this.promptData = false;
	},
	
	
	
	
	
	showError: function (msg, cb) {
		if (typeof cb == 'function')
			cb = cb.bind(this);
		else
			cb = false;
		this.errorData = {'cb':cb};
		show_error(msg, 'JSO_'+this.uid+'.showErrorCallback.bind(JSO_'+this.uid+')');
		return;
	},
	showErrorCallback: function () {
		if (this.errorData == false)
			return;
		if (typeof this.errorData.cb != 'function')
			return;
		this.errorData.cb();
		this.errorData = false;
		return;
	},
	
	
	showNotice: function (msg) {
		MochaUI.notification(msg);
	},
	
	startIncrementalLoad: function (component, action, token, callback) {
		if (
			!$chk(token) ||
			!$chk(component) || 
			!$chk(action) || 
			!$chk(callback) 
		) {
			return false;
		}
		var inc = {
			'token': token,
			'callback': callback,
			'action': action,
			'component': component
		}
		if (typeof this.incrementals == 'undefined')
			this.incrementals = new Hash();
		this.incrementals.include(token, inc);
		jsonreq(component, 'action='+action+'&token='+token, this.incrementalLoadCallback.bind(this));
		return true;
	},
	incrementalLoadCallback: function (resp) {
		if (!$chk(resp))
			return false;
		if ($chk(resp.err)) {
			this.showError('Error during incremental load: '+resp.msg);
			return false;
		}
		if (!$chk(resp.token)) {
			this.showError('Error during incremental load: token not specified in response, cannot handle');
			return false;
		}
		if (typeof this.incrementals == 'undefined') {
			this.showError('Error during incremental load: internal error');
			return false;
		}
		if (!this.incrementals.has(resp.token)) {
			this.showError('Error during incremental load: asked to handle unknown token');
			return false;
		}
		this.incrementals[resp.token].callback(resp);
		
		if (resp.incremental_complete) {
			this.incrementals.erase(resp.token);
			return true;
		}
		
//		console.log('not complete, about to make another incremental callout')
		jsonreq(this.incrementals[resp.token].component, 'action='+this.incrementals[resp.token].action+'&token='+resp.token, this.incrementalLoadCallback.bind(this));
		return true;
	}
});








String.noLC = new Object
  ({the:1, a:1, an:1, and:1, or:1, but:1, aboard:1,
    about:1, above:1, across:1, after:1, against:1,
    along:1, amid:1, among:1, around:1, as:1, at:1,
    before:1, behind:1, below:1, beneath:1, beside:1,
    besides:1, between:1, beyond:1, but:1, by:1, 'for':1,
    from:1, 'in':1, inside:1, into:1, like:1, minus:1,
    near:1, of:1, off:1, on:1, onto:1, opposite:1,
    outside:1, over:1, past:1, per:1, plus:1,
    regarding:1, since:1, than:1, through:1, to:1,
    toward:1, towards:1, under:1, underneath:1, unlike:1,
    until:1, up:1, upon:1, versus:1, via:1, 'with':1,
    within:1, without:1});

String.prototype.titleCase = function () {
  var parts = this.split(' ');
  if ( parts.length == 0 ) return '';

  var fixed = new Array();
  for ( var i in parts ) {
    var fix = '';
    if (typeof parts[i] == 'string') {
	    if ( String.noLC[parts[i]] )
	    {
	      fix = parts[i].toLowerCase();
	    }
	    else if ( parts[i].match(/^([A-Z]\.)+$/i) )
	    { // will mess up "i.e." and like
	      fix = parts[i].toUpperCase();
	    }
	    else if ( parts[i].match(/^[^aeiouy]+$/i) )
	    { // voweless words are almost always acronyms
	      fix = parts[i].toUpperCase();
	    }
	    else
	    {
	      fix = parts[i].substr(0,1).toUpperCase() +
	                 parts[i].substr(1,parts[i].length);
	    }
	    fixed.push(fix);
	}
  }
  fixed[0] = fixed[0].substr(0,1).toUpperCase() +
                 fixed[0].substr(1,fixed[0].length);
  return fixed.join(' ');
}


/* END site/base
*************************************************************/

/**************************************************************
 BEGIN site/dialog                                           */
var newDialog = new Class ({
	initialize: function (options) {

	}
});




var dialog = new Class({
  Implements: [Chain],

	getOptions: function(){
		return {
			name: 'dialog',
			zIndex: 999,
			onReturn: false,
			onReturnFunction : $empty,
			BoxStyles: {
				'width': 500
			},
			OverlayStyles: {
				'background-color': '#000',
				'opacity': 0.7
			},
			showDuration: 200,
			showEffect: Fx.Transitions.linear,
      		closeDuration: 100,
			closeEffect: Fx.Transitions.linear,
			moveDuration: 500,
			moveEffect: Fx.Transitions.Back.easeOut,
			onShowStart : $empty,
			onShowComplete : $empty,
			onCloseStart : $empty,
			onCloseComplete : function(properties) {
				this.options.onReturnFunction(this.options.onReturn);
			}.bind(this)
		};
	},

	initialize: function(options){
    	this.i=0;
		this.is_wait = false;
    
		this.setOptions(this.getOptions(), options);

		this.Overlay = new Element('div', {
			'id': 'BoxOverlay',
			'styles': {
				'display': 'none',
				'z-index': this.options.zIndex,
				'position': 'absolute',
				'top': '0',
				'left': '0',
				'background-color': this.options.OverlayStyles['background-color'],
				'opacity': 0,
				'height': window.getScrollHeight() + 'px',
				'width': window.getScrollWidth() + 'px'
			}
		});

		this.Content = new Element('div', {
			'id': this.options.name + '-BoxContainer'
		});

	    this.Container = new Element('div', {
	      'id': this.options.name + '-BoxContent'
	    }).adopt(this.Content);

		this.InBox = new Element('div', {
			'id': this.options.name + '-InBox'
		}).adopt(this.Container);;
		
		this.Box = new Element('div', {
			'id': this.options.name + '-Box',
			'styles': {
				'display': 'none',
				'z-index': this.options.zIndex + 2,
				'position': 'absolute',
				'top': '0',
				'left': '0',
				'width': this.options.BoxStyles['width'] + 'px'
			}
		}).adopt(this.InBox);

    	this.Overlay.injectInside(document.body);
    	this.Box.injectInside(document.body);

    	this.preloadImages();
    
		window.addEvent('resize', function() {
			if(this.options.display == 1) {
				this.Overlay.setStyles({
					'height': window.getScrollHeight() + 'px',
					'width': window.getScrollWidth() + 'px'
				});
				this.replaceBox();
			}
			if(this.DisplayBox == 1) {
				this.Overlay.setStyles({
					'height': window.getScrollHeight() + 'px',
					'width': window.getScrollWidth() + 'px'
				});
				this.replaceDisplayBox();
			}
		}.bind(this));
		
		window.addEvent('scroll', this.replaceBox.bind(this));
		window.addEvent('scroll', this.replaceDisplayBox.bind(this));
	},
	
	setOverlaySize: function () {
		if (this.Overlay) {
			this.Overlay.setStyle('height', document.getScrollSize().y);
			this.Overlay.setStyle('width', document.getScrollSize().x);
		}
	},
	
	preloadImages: function() {
		//var img = new Array(2);
		//img[0] = new Image();img[1] = new Image();img[2] = new Image();
		//img[0].src = this.Box.getStyle('background-image').replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1");
		//img[1].src = this.InBox.getStyle('background-image').replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1");
		//img[2].src = this.Container.getStyle('background-image').replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1");
	},


	/*
	Property: display
		Show or close box
		
	Argument:
		option - integer, 1 to Show box and 0 to close box (with a transition).
	*/	
	display: function(option){
		if(this.Transition)
			this.Transition.cancel();				

		// Show Box	
		if(this.options.display == 0 && option != 0 || option == 1) {

			if(Browser.Engine.trident4)
				$$('select', 'object', 'embed').each(function(node){ node.style.visibility = 'hidden' });

			this.Overlay.setStyle('display', 'block');
			this.options.display = 1;
			this.fireEvent('onShowStart', [this.Overlay]);

			this.Transition = new Fx.Tween(this.Overlay,
				{
          			property: 'opacity',
					duration: this.options.showDuration,
					transition: this.options.showEffect,
					onComplete: function() {
						sizes = window.getSize();
						scrollito = window.getScroll();
						this.Box.setStyles({
							'display': 'block',
							'left': (scrollito.x + (sizes.x - this.options.BoxStyles['width']) / 2).toInt()
						});
						this.replaceBox();
						this.fireEvent('onShowComplete', [this.Overlay]);
					}.bind(this)
				}
			).start(this.options.OverlayStyles['opacity']);
			this.i++;

		} else { // Close Box
			if(Browser.Engine.trident4)
				$$('select', 'object', 'embed').each(function(node){ node.style.visibility = 'visible' });

  			this.queue.delay(500,this);

			this.Box.setStyles({
				'display': 'none',
				'top': 0
			});
			this.Content.empty();
			this.options.display = 0;
						
			this.fireEvent('onCloseStart', [this.Overlay]);
			
			if(this.i==1) {
				this.Transition = new Fx.Tween(this.Overlay,
					{
						property: 'opacity',
						duration: this.options.closeDuration,
						transition: this.options.closeEffect,
						onComplete: function() {
						this.fireEvent('onCloseComplete', [this.Overlay]);
						}.bind(this)
					}
				).start(0);
				this.i--;
			}
		}
	},

	/*
	Property: replaceBox
		Move Box in screen center when brower is resize or scroll
	*/
	replaceBox: function() {
		if(this.options.display == 1) {
			sizes = window.getSize();
      scrollito = window.getScroll();

			if(this.MoveBox)
				this.MoveBox.cancel();
			
			this.MoveBox = new Fx.Morph(this.Box, {
				duration: this.options.moveDuration,
				transition: this.options.moveEffect
			}).start({

				'left': (scrollito.x + (sizes.x - this.options.BoxStyles['width']) / 2).toInt(),
				'top': (scrollito.y + (sizes.y - this.Box.offsetHeight) / 2).toInt()

			});

		}
	},


	queue: function() {
		if (this.i > 0) {
			this.i--;
			this.callChain();
		}
	},

	/*
	Property: messageBox
		Core system for show all type of box
		
	Argument:
		type - string, 'alert' or 'confirm' or 'prompt'
		message - text to show in the box
		properties - see Options below
		input - text value of default 'input' when prompt
		
	Options:
		textBoxBtnOk - text value of 'Ok' button
		textBoxBtnCancel - text value of 'Cancel' button
		onComplete - a function to fire when return box value
	*/	
	messageBox: function(type, message, properties, input) {
		this.setOverlaySize();
		this.chain(function () {

      properties = $extend({
        'textBoxBtnOk': 'OK',
        'textBoxBtnCancel': 'Cancel',
        'textBoxInputPrompt': null,
        'password': false,
        'onComplete': $empty
      }, properties || {});


      this.options.onReturnFunction = properties.onComplete;

      this.ContainerButtons = new Element('div', {
        'id': this.options.name + '-Buttons'
      });
      


      if(type == 'alert' || type == 'info' || type == 'error')
      {
          this.AlertBtnOk = new Element('input', {
            'id': 'BoxAlertBtnOk',
            'type': 'submit',
            'value': properties.textBoxBtnOk,
            'styles': {
              'width': '70px'
            }
          });
          this.AlertBtnOk.addEvent('click', function() {
            this.options.onReturn = true;
            Dialog.hide();
          }.bind(this));
        
          if(type == 'alert')
            this.clase = 'BoxAlert';
          else if(type == 'error')
            this.clase = 'BoxError';
          else if(type == 'info')
            this.clase = 'BoxInfo';
        
          this.Content.setProperty('class',this.clase).set('html',message);

          this.AlertBtnOk.injectInside(this.ContainerButtons);

          this.ContainerButtons.injectInside(this.Content);
          this.display(1);
      }
	  else if(type == 'show')
      {
          this.Content.set('html',message);
          this.display(1);
      }
	  else if(type == 'wait')
      {
		  if (typeof message == 'undefined' || message.length < 1)
		  		message = 'Please wait....';
      	  this.is_wait = true;
          this.Content.set('html','<img src="'+SITE_URLS+'site/img/system/small_loading.gif" style="" alt="Please wait..." /> '+message);
          this.clase = '';
          this.Content.className = '';
          this.display(1);
          this.advance_loading_ani();
      }
      else if(type == 'confirm')
      {
          this.ConfirmBtnOk = new Element('input', {
            'id': 'BoxConfirmBtnOk',
            'type': 'submit',
            'value': properties.textBoxBtnOk,
            'styles': {
              'width': '70px'
            }
          });

          this.ConfirmBtnCancel = new Element('input', {
            'id': 'BoxConfirmBtnCancel',
            'type': 'submit',
            'value': properties.textBoxBtnCancel,
            'styles': {
              'width': '70px'
            }
          });

          this.ConfirmBtnOk.addEvent('click', function() {
            this.options.onReturn = true;
            this.display(0);
          }.bind(this));

          this.ConfirmBtnCancel.addEvent('click', function() {
            this.options.onReturn = false;
            this.display(0);
          }.bind(this));

          this.Content.setProperty('class','BoxConfirm').set('html',message);

          this.ConfirmBtnOk.injectInside(this.ContainerButtons);
          this.ConfirmBtnCancel.injectInside(this.ContainerButtons);
          
          this.ContainerButtons.injectInside(this.Content);
          this.display(1);
      }
      else if(type == 'prompt')
      {
          this.PromptBtnOk = new Element('input', {
            'id': 'BoxPromptBtnOk',
            'type': 'submit',
            'value': properties.textBoxBtnOk,
            'styles': {
              'width': '70px'
            }
          });

          this.PromptBtnCancel = new Element('input', {
            'id': 'BoxPromptBtnCancel',
            'type': 'submit',
            'value': properties.textBoxBtnCancel,
            'styles': {
              'width': '70px'
            }
          });
          
          type = properties.password ? 'password' : 'text';
          this.PromptInput = new Element('textarea', {
            'id': 'BoxPromptInput',
            'type': type,
            'value': input,
            'styles': {
              'width': '250px',
              'height': '80px'
            }
          });

          this.PromptBtnOk.addEvent('click', function() {
            this.options.onReturn = this.PromptInput.value;
            this.display(0);
          }.bind(this));

          this.PromptBtnCancel.addEvent('click', function() {
            this.options.onReturn = false;
            this.display(0);
          }.bind(this));

          this.Content.setProperty('class','BoxPrompt').set('html',message + '<br />');
          this.PromptInput.injectInside(this.Content);
          new Element('br').injectInside(this.Content);
          this.PromptBtnOk.injectInside(this.ContainerButtons);
          this.PromptBtnCancel.injectInside(this.ContainerButtons);


          this.ContainerButtons.injectInside(this.Content);

          this.display(1);
      }
      else
      {
          this.options.onReturn = false;
          this.display(0);		
      }

    });

		this.i++;

		if(this.i==1) this.callChain();

	},

	advance_loading_ani: function () {
	
		if ($('loading_ani') == null)
			return;
	
		if ($('loading_ani').getSize().x >= $('loading_container').getSize().x) {
			$('loading_ani').setStyle('width', '0px');
		} else {
			var new_width_increment = (($('loading_container').getStyle('width').replace('px', '')) / 50);
			var new_width = ($('loading_ani').getSize().x + new_width_increment) + 'px'
			$('loading_ani').setStyle('width', new_width);
		} 
		setTimeout('Dialog.advance_loading_ani()', 100);
	},

	/*
	Property: hide
		Shortcut for hiding the dialog
		
	Argument:
		none
	*/		
	hide: function(message, properties){
		this.display(0);
	},

	/*
	Property: show
		Shortcut for show
		
	Argument:
		properties - see Options in messageBox
	*/		
	show: function(message, properties){
		this.messageBox('show', message, properties);
	},


	/*
	Property: wait
		Shortcut for wait
		
	Argument:
		properties - see Options in messageBox
	*/		
	please_wait: function(message, properties){
		this.messageBox('wait', message);
	},

	/*
	Property: alert
		Shortcut for alert
		
	Argument:
		properties - see Options in messageBox
	*/		
	alert: function(message, properties){
		this.messageBox('alert', message, properties);
	},

	/*
	Property: info
		Shortcut for alert info
		
	Argument:
		properties - see Options in messageBox
	*/		
	info: function(message, properties){
		this.messageBox('info', message, properties);
	},

	/*
	Property: error
		Shortcut for alert error
		
	Argument:
		properties - see Options in messageBox
	*/		
	error: function(message, properties){
		this.messageBox('error', message, properties);
	},

	/*
	Property: confirm
		Shortcut for confirm
		
	Argument:
		properties - see Options in messageBox
	*/
	confirm: function(message, properties){
		this.messageBox('confirm', message, properties);
	},

	/*
	Property: prompt
		Shortcut for prompt
		
	Argument:
		properties - see Options in messageBox
	*/	
	prompt: function(message, input, properties){
		this.messageBox('prompt', message, properties, input);
	},
	
	
	box : function (message, properties, message_type, message_source_ele, show_overlay, auto_move) {
		this.setOverlaySize();
		var box_div = new Element('div', {'id': 'box_box_div'});
		var content_div = new Element('div', {
			'id': 'box_content_div',
			'styles': {
				'width': '100%',
				'overflow':'auto'
			}
		});
		var closer_div = new Element('div', {
			'id': 'box_closer_div',
			'styles': {
				'width':  '24px',
				'height': '24px',
				'backgroundImage': 'url('+SITE_URLS+'site/img/dialog/close.png)',
				'backgroundRepeat': 'no-repeat',
				'float': 'right',
				'marginTop': '-30px',
				'marginRight': '-30px',
				'cursor': 'pointer'
			}
		});
		closer_div.addEvent('click', function () {
			Dialog.removeDisplayBox();
		});
		closer_div.injectInside(box_div);
		content_div.injectInside(box_div);
		box_div.injectInside(document.body);	
		
		if (typeof show_overlay == 'undefined')
			show_overlay = true;
	
		if (typeof auto_move == 'undefined')
			auto_move = true;
	
		properties = $extend({
			'left': '50%',
			'width': '600px',
			'height': '550px',
			'marginLeft': '-400px',
			'backgroundColor':'white',
			'borderTop': '20px solid black',
			'borderBottom': '20px solid black',
			'borderLeft': '20px solid black',
			'borderRight': '20px solid black',
			'position':'absolute',
			'zIndex':'999',
			'visibility': 'hidden'
		}, properties || {});
		
		
		if (typeof message_type == 'undefined') {
			content_div.set('html', message);
			Form.init_forms(true);
		} else if (message_type == 'link') {
			var content_iframe = new Element('iframe', {
				'id': 'DisplayBoxIF',
				'frameborder':'0',
				'styles' : {
					'padding' : '0 0 0 0',
					'margin' : '0 0 0 0' 
				}
			});
			content_iframe.set('src', message);
			content_iframe.store('overall', box_div);
			content_iframe.injectInside(content_div);
			content_iframe.setStyle('height', '99%');
			content_iframe.setStyle('width', '100%');
			content_iframe.setStyle('border', '0');
			content_iframe.addEvent('load', function () {
        		var rv = null; 
				// if contentDocument exists, W3C compliant (e.g. Mozilla) 
				if (this.contentDocument) {
					rv = this.contentDocument;
					//this.retrieve('overall').setStyle('height', rv.body.scrollHeight - 5);
				} else { // bad Internet Explorer  ;)
					//rv = document.frames['DisplayBoxIF'].document;
				}
				//this.setStyle('height', rv.body.scrollHeight);
				//this.retrieve('overall').setStyle('height', rv.body.scrollHeight - 5);
			})
		} else if (message_type == 'img') {
			var content_img = new Element('img');
			content_img.set('src', message);
			content_img.injectInside(content_div);
			
			content_div.setStyle('overflow', 'hidden');
			
			var content_title = new Element('p');
			content_title.setStyle('width', '100%');
			content_title.setStyle('height', '100%');
			content_title.setStyle('paddingBottom', '20px');
			content_title.setStyle('paddingTop', '10px');
			content_title.setStyle('backgroundColor', 'black');
			content_title.setStyle('color', 'white');
			content_title.set('html', '<br>'+message_source_ele.get('alt'));
			content_title.injectInside(content_div);
			content_title.addClass('mediumText');
			
			properties.height = (content_img.getSize().y + 10 + content_title.getSize().y) + 'px';
			properties.width = content_img.getSize().x + 'px';
		} else {
			content_div.set('html', message);
			Form.init_forms(true);
		}



		// get vertical center
		if (typeof properties.top == 'undefined') {
			var bheight = properties.height.replace('px', '');
			var center_start = ((document.body.getSize().y) / 2) - (bheight / 2) + 'px';
			properties.top = center_start;
		}

		// recalculate horizontal center
		var bwidth = properties.width.replace('px', '');
		var margin_start = (bwidth / 2) + 'px';
		properties.marginLeft = '-' + margin_start;
		
		
		for (i in properties) {
			box_div.setStyle(i, properties[i]);
		}
		
		this.DisplayBoxStyles = properties;
		this.DisplayBoxContainer = box_div;
		this.DisplayBox = 1;

		
		content_div.setStyle('height', box_div.clientHeight);
		

		if (show_overlay) {
			this.Overlay.setStyle('display', 'block');
			this.Transition = new Fx.Tween(this.Overlay,
				{
	     			property: 'opacity',
					duration: this.options.showDuration,
					transition: this.options.showEffect,
					onComplete: function() {
						if (auto_move)
							this.showDisplayBox();
						else
							this.DisplayBoxContainer.setStyle('visibility', 'visible');
					}.bind(this)
				}
			).start(this.options.OverlayStyles['opacity']);
		} else {
			if (auto_move)
				this.showDisplayBox();
			else
				this.DisplayBoxContainer.setStyle('visibility', 'visible');
		}
	},
	
	showDisplayBox: function (moveDirection) {	
		if (typeof moveDirection == 'undefined') {
			sizes = window.getSize();
	  		scrollito = window.getScroll();
			this.DisplayBoxContainer.setStyle('top', ((scrollito.y + (sizes.y - 550) / 2).toInt()) + 'px');
			this.DisplayBoxContainer.setStyle('height', '0px');
			this.DisplayBoxContainer.setStyle('width', '0px');
			this.DisplayBoxContainer.setStyle('visibility', 'visible');
			this.chain(function () {
				this.Transition = new Fx.Tween(this.DisplayBoxContainer,
					{
		     			property: 'width',
						duration: this.options.showDuration,
						transition: this.options.showEffect,
						onComplete: function() {
							this.showDisplayBox(1);
						}.bind(this)
					}
				).start(this.DisplayBoxStyles.width);
			});
		} else {
			this.chain(function () {
				this.Transition = new Fx.Tween(this.DisplayBoxContainer,
					{
		     			property: 'height',
						duration: this.options.showDuration,
						transition: this.options.showEffect,
						onComplete: function() {
							if ($chk(this.DisplayBoxStyles.onOpen) && typeof this.DisplayBoxStyles.onOpen == 'function')
								this.DisplayBoxStyles.onOpen();
						}.bind(this)
					}
				).start(this.DisplayBoxStyles.height);
			});
		}
		this.callChain();
	},
	
	removeDisplayBox: function (moveDirection) {
		if (typeof moveDirection != 'undefined') {
			this.chain(function () {
				this.Transition = new Fx.Tween(this.DisplayBoxContainer,
					{
		     			property: 'width',
						duration: this.options.showDuration,
						transition: this.options.showEffect,
						onComplete: function() {
							this.DisplayBoxContainer.destroy();
							this.Transition = new Fx.Tween(this.Overlay,
								{
					     			property: 'opacity',
									duration: this.options.showDuration,
									transition: this.options.showEffect,
									onComplete: function() {
										if ($chk(this.DisplayBoxStyles.onClose) && typeof this.DisplayBoxStyles.onClose == 'function')
											this.DisplayBoxStyles.onClose();
										this.Overlay.setStyle('display', 'none');
										this.DisplayBox = 0;
										this.DisplayBoxProperties = {};
									}.bind(this)
								}
							).start(0);	
						}.bind(this)
					}
				).start(0);
			});
		} else {
			if ($chk(this.DisplayBoxStyles.beforeClose)) {
				var resp = this.DisplayBoxStyles.beforeClose();
				if (resp == false)
					return;
			}
			this.chain(function () {
				this.Transition = new Fx.Tween(this.DisplayBoxContainer,
					{
		     			property: 'height',
						duration: this.options.showDuration,
						transition: this.options.showEffect,
						onComplete: function() {
							this.removeDisplayBox(1);						
						}.bind(this)
					}
				).start(0);
			});
		}
		this.callChain();
	},
	
	/*
	Property: replaceDisplayBox
		Move DisplayBox in screen center when brower is resize or scroll
	*/
	replaceDisplayBox: function() {
		if(this.DisplayBox == 1) {
			sizes = window.getSize();
      		scrollito = window.getScroll();

			if(this.MoveDisplayBox)
				this.MoveDisplayBox.cancel();
			
			this.MoveDisplayBox = new Fx.Morph(this.DisplayBoxContainer, {
				duration: this.options.moveDuration,
				transition: this.options.moveEffect
			}).start({
				'top': (scrollito.y + (sizes.y - this.DisplayBoxContainer.offsetHeight) / 2).toInt()
			});
		}
	}	
});

dialog.implement(new Events, new Options);


window.addEvent('domready', function() {
	Dialog = new dialog();
	
	
	NewDialog = new newDialog();
	
	var db_elements = $(document.body).getElements('a[rel*=DisplayBox]');
	for (var z=0 ; z<db_elements.length; z++) {
		db_elements[z].addEvent('click', function () {
			Dialog.box(this.get('href'), {}, 'link', this);
			return false;		
		});
	}
	
	var db_elements = document.body.getElements('img[rel*=DisplayBox]');
	for (var z=0 ; z<db_elements.length; z++) {
		db_elements[z].setStyle('cursor', 'pointer');
		db_elements[z].addEvent('click', function () {
			Dialog.box(this.get('src'), {}, 'img', this);
			return false;
		});
	}
	
	window.addEvent('resize', function () {
		Dialog.setOverlaySize();
	});

});


/**
 * Show please wait popup
 *
 * @author	Chris Baker
 *
 * @access	public
 *
 * @version 0.3-11
*/
function show_please_wait (msg) {
	if (typeof msg == 'undefined' || !$chk(msg))
		msg = 'Please wait...';
	Dialog.hide()
	Dialog.please_wait(msg);
	return false;
}
/**
 * Hide the 'Please Wait' dialog
 *
 * @author	Chris Baker
 *
 * @access	public
 *
 * @version 0.3-11
*/
function hide_please_wait () {
	Dialog.hide()
	return false;
}

/**
 * Show html in a popup
 *
 * @author	Chris Baker
 *
 * @access	public
 *
 * @version 0.3-11
*/
function show_html (html) {
	Dialog.hide()
	Dialog.show(html);
	return false;
}


/**
 * Show alert_text in a dialog with an ok button and a warning icon
 *
 * @author	Chris Baker
 *
 * @access	public
 *
 * @version 0.3-11
*/
function show_alert (alert_text) {
	Dialog.hide()
	Dialog.alert(alert_text);
	return false;
}

/**
 * Show err_text in a dialog with an ok button and an error icon
 *
 * @author	Chris Baker
 *
 * @access	public
 *
 * @version 0.3-11
*/
function show_error (err_text, callback) {
	Dialog.hide()
	if (typeof callback != 'undefined') {
		Dialog.error(err_text, { onComplete:function() {
			eval(callback + '()')
		}});
	} else {
		Dialog.error(err_text);
	}
	return false;
}

/**
 * Show conf_text in a dialog with an ok button and a lightbulb icon
 *
 * @author	Chris Baker
 *
 * @access	public
 *
 * @version 0.3-11
*/
function show_info (info_text, callback) {
	Dialog.hide()
	if (typeof callback != 'undefined') {
		Dialog.info(info_text, { onComplete:function() {
			eval(callback + '()')
		}});
	} else {
		Dialog.info(info_text);
	}
	return false;
}

/**
 * Show confirm_text in a dialog with yes and no button with a question icon
 *   - will return the result as boolean
 *
 * @author	Chris Baker
 *
 * @access	public
 *
 * @version 0.3-11
*/
function show_confirm (confirm_text, callback, buttons) {
	if (!$chk(buttons))
		buttons = {'textBoxBtnOk':'OK', 'textBoxBtnCancel':'Cancel'};
	Dialog.hide()
 	Dialog.confirm(confirm_text, { onComplete:function(question_return) {
		eval(callback + '(question_return)')
	}, 
	textBoxBtnOk: buttons.textBoxBtnOk,
	textBoxBtnCancel: buttons.textBoxBtnCancel
	});
	return false;
}

/**
 * Show confirm_text in a dialog with yes and no button with a question icon
 *   - will return the result as boolean
 *
 * @author	Chris Baker
 *
 * @access	public
 *
 * @version 0.3-11
*/
function show_question (ques_text, callback) {
	Dialog.hide()
 	Dialog.prompt(ques_text, '', { onComplete:function(question_return) {
		eval(callback + '(question_return)')
	}});
	return false;
}


/**
 * Displays a full-screen black div with opacity, referred herein as 'blind'
 *
 * @author	Chris Baker
 *
 * @access	public
 *
 * @version 0.3-11
*/
function show_blind() {
	Dialog.hide();
	if ($('BoxOverlay')) {
		$('BoxOverlay').style.display = 'block';
		$('BoxOverlay').style.visibility = 'visible';
		$('BoxOverlay').tween('opacity', '.7');
	} else {
		Dialog.initialize();
		show_blind();		
	}
	return false;
}

/**
 * Remove the blind and any dialogs from the document
 *
 * @author	Chris Baker
 *
 * @access	public
 *
 * @version 0.3-11
*/
function hide_blind () {
	Dialog.hide();
	if ($('BoxOverlay')) {
		$('BoxOverlay').style.visibility = 'hidden';
	}
	return false;
}


/* END site/dialog
*************************************************************/

/**************************************************************
 BEGIN site/form                                           */
// base function for form edit event
function form_edit(){
	//console.log('form edit called');
}
// base function for form validation (called onSubmit)
function validate_form(){}


var ___page_has_dp = false;
var ___page_has_dtp = false;
var ___page_has_tp = false;
var ___formCityState = false;

var FormClass = new Class({
	getOptions: function(){
		return {
			'adjust_closers' : false	
		};
	},
	initialize: function(options){
		// get an array of all forms
		if (!$('body'))
			return;

	    this.forms = $('body').getElements('form');

	    if (this.forms.length == 0)
	    	return false;

		this.adjust_closers();
		this.CityStateUpdater = CityStateUpdater;
		this.init_forms();	
		this.set_hints();
		this.set_limits();
	},

	adjust_closers: function () {
		var closers = $$('.formDescriptionCloser');
		$each(closers, function(ele, key){
			ele.setStyle('border', '0').setStyle('margin', '0');
	    	ele.setStyle('top', parseFloat(ele.getNext().offsetTop) - 50 );
	    	ele.setStyle('left', (parseFloat(ele.getNext().offsetLeft) +  parseFloat(ele.getNext().clientWidth)) - 150);
	    	ele.show();
		});	
	},

	add_error_to_field: function (field_id, error_text) {
		var field = $(field_id);
		if (!field)
			return false;

		var fparent = field.getParent('div[class*=formElementContainer]');
		if (!fparent)
			return false;

		err_msg_ele = fparent.getElement('p.formErrorField');
		if ($chk(err_msg_ele))
			return false;

		if (typeof error_text == 'undefined')
			error_text = 'Undefined field error!';

		var err = new Element('p', {
			'class': 'formErrorField',
			'html': '<strong>&raquo;&nbsp;' + error_text + '</strong>'
		});

		fparent.addClass('withError');
		err.fade('hide');
		err.inject(fparent, 'top');
		err.fade('in');
		return false;
	},
	remove_field_errors: function (field_id) {
		if (typeof field_id != 'undefined') {
			if (!$(field_id))
				return false;
			var target = $(field_id).getParent();
			target.removeClass('withError');
			err_msg_ele = target.getElement('p.formErrorField');
			if ($chk(err_msg_ele)) {
				var rme = new Fx.Tween(err_msg_ele, {'duration ':'short'});
				rme.addEvent('complete', function (e) {
					e.dispose();
				});
				rme.start('opacity', '1', '0');
			}
			return false;
		}
		$each($$('.withError'), function (ele) {
			ele.removeClass('withError');
			err_msg_ele = ele.getElement('p.formErrorField');
			if ($chk(err_msg_ele)) {
				var rme = new Fx.Tween(err_msg_ele, {'duration ':'short'});
				rme.addEvent('complete', function (e) {
					e.dispose();
				});
				rme.start('opacity', '1', '0');
			}
		});
		return false;
	},	
	
	/*******************************************************************************
	 * Function: 	none - form element focus/blur listeners
	 *
	 * This code executes on the event DOMREADY, it adds a blur and focus event
	 * to all form inputs.  The events add/remove a yellow hilite to a field when it
	 * gains/loses focus.
	 * 
	 * It also hides the field hints, which are then displayed when the user clicks
	 * a particular field. A focus/blur event is added to show/hide the hints
	 * 
	 * Parameters: 	none
	 *   
	 * Author:  	Chris Baker
	 * Date:		01-21-2009
	  ******************************************************************************/
	init_forms: function (rescan_for_new_forms) {
		$each($$('.folded_fieldset'), function (ele) {
			ele.getElements('div[class*=formElementContainer]').hide();
		});

		//console.log('INIT FORMS');
		//console.log('Form.init_form - active');
		if (typeof rescan_for_new_forms != 'undefined')
			this.forms = $('body').getElements('form');

	    // loop each form
	    for (var i=0; i <this.forms.length; i++) {
			//console.log('Form.init_form - checking');
			//console.log(this.forms[i]);

			//console.log('Form.init_form - will init');
	    	// container for all elements within this form
	        var these_children = this.forms[i].elements;
	        
	        if (typeof these_children == 'undefined' || these_children.length == 0)
	        	break;
	
	        // loop each element
			this.init_elements(these_children);
		        
	    	this.forms[i].store('jso_initialized', true);
	    } // end :: for looping all forms 
	},

	init_elements: function (eles) {
        for (var z=0; z<eles.length; z++) {
        	// grab a moo element for this child
        	var this_child = $(eles[z]);

			if (this_child.retrieve('jso_initialized') == null) {
	        	// skip fieldsets, buttons, and radios
	            if (this_child.tagName != 'FIELDSET' && this_child.tagName != 'BUTTON' && this_child.type != 'radio') {
	                
					// check if it is a date element
					if (this_child.hasClass('datefield')) {
						var dp_options = false;

						if (this_child.getPrevious().hasClass('dp_options')) {
							var dp_optionsR = this_child.getPrevious().get('html');
							dp_optionsR = dp_optionsR.split('|');
							dp_options = {};
							$each(dp_optionsR,function(d){d = d.split(':');dp_options[d[0]] = d[1];});
							//console.log(dp_options)
						} else {
							dp_options = this_child.retrieve('dp_options');
						}							
						if (!dp_options) {
							dp_options = {};
						}
						var dp = new dateTimePicker(this_child, dp_options);
						this_child.store('jso_ref', dp);
					}
					// check if it is a time element
					if (this_child.hasClass('timefield')) {
						var dp_options = false;
						if (this_child.getPrevious().hasClass('dp_options')) {
							var dp_optionsR = this_child.getPrevious().get('html');
							dp_optionsR = dp_optionsR.split('|');
							dp_options = {};
							$each(dp_optionsR,function(d){d = d.split(':');dp_options[d[0]] = d[1];});
							//console.log(dp_options)
						} else {
							dp_options = this_child.retrieve('dp_options');
						}							
						if (!dp_options) {
							dp_options = {};
						}
						var dp = new dateTimePicker(this_child, dp_options);
						this_child.store('jso_ref', dp);
					}
					// check if it is a date/time element
					if (this_child.hasClass('date_timefield')) {
						var dp_options = false;
						if (this_child.getPrevious().hasClass('dp_options')) {
							var dp_optionsR = this_child.getPrevious().get('html');
							dp_optionsR = dp_optionsR.split('|');
							dp_options = {};
							$each(dp_optionsR,function(d){d = d.split(':');dp_options[d[0]] = d[1];});
							//console.log(dp_options)
						} else {
							dp_options = this_child.retrieve('dp_options');
						}							
						if (!dp_options) {
							dp_options = {};
						}
						var dp = new dateTimePicker(this_child, dp_options);
						this_child.store('jso_ref', dp);
					}
					if (this_child.hasClass('cash_format')) {
						this_child.addEvent('blur', function () {
							this.value = cash_format(this.value);
						});
						this_child.addEvent('change', function () {
							this.value = cash_format(this.value);
						});
						this_child.set('value', cash_format(this_child.get('value')));
					}
					if (this_child.hasClass('number_format')) {
						this_child.addEvent('blur', function () {
							var c = stripNonNumeric(this.value);
							if (c.length > 0)
								this.value = c;
							else
								this.value = 0;
						});
						this_child.addEvent('change', function () {
							var c = stripNonNumeric(this.value);
							if (c.length > 0)
								this.value = c;
							else
								this.value = 0;
						});
					}
					// add the events, both toggle the class withFocus to achieve the yellow bg/border
					this_child.addEvent('focus', function () {
						if ($(this.parentNode).hasClass('formElementContainer'))
							$(this.parentNode).addClass('withFocus');
	                });
	                this_child.addEvent('blur', function () {
	                	if ($(this.parentNode).hasClass('withFocus'))
	                		$(this.parentNode).removeClass('withFocus');
	                });
	                
	                var chk_help = this_child.getNext('span[class*=field_help]');
					if (chk_help && chk_help.get('html').length == 0)
						chk_help.hide(); 
				
				// radios get a special case, because of their element container           
	            } else if (this_child.type == 'radio') {
	
					// each event must cycle upward to find the formElementContainer,
					// which is the element we want to apply the css class to.
	                this_child.addEvent('focus', function () {
	                
		            	var find_parent = $(this.parentNode);
		            	var found_parent = false;
		            	
		            	// check the next 6 parents, we're after the one with the
		            	// classname 'formElementContainer'
		            	for (var t=0;t<6;t++) {
							if (find_parent && find_parent.get('class').indexOf('formElementContainer') != -1) {
								find_parent.addClass('withFocus');
								found_parent = true;
								break;
							}	
							find_parent = $(find_parent.parentNode);					
						}
	                });
	                this_child.addEvent('blur', function () {
		            	var find_parent = $(this.parentNode);
		            	var found_parent = false;
		            	for (var t=0;t<6;t++) {
							if (find_parent && find_parent.get('class').indexOf('formElementContainer') != -1) {
								find_parent.removeClass('withFocus');
								found_parent = true;
								break;
							}					
							find_parent = $(find_parent.parentNode);					
						}
	                });
				} // end :: if checking element type
			} // end :: if checking if this element has been jso_initialized
        	this_child.store('jso_initialized', true);					
        } // end :: for looping form elements
	},
	
	
	/**
	 * Textarea/input limit handler
	 * Adds an onKeyUp event to every input and textarea element with
	 * rel="limit" and maxlength="x" attributes, adds a counter below the element
	 *  that informs the user of the remaining characters for the field
	 *
	 *  I.E.
	 *  Limit: 30/Remaining: 30
	 *
	 * As the user types, the 'remaining' count updates with the new count
	 */
	set_limits: function () {
		if (!$('body'))
			return false;	
		// grab a list of all limited fields
		var textareas = $('body').getElements('input[rel*=limit]');
		var inputs = $('body').getElements('textarea[rel*=limit]');
		inputs = mergeArrays(inputs, textareas);
	
		// loop through the fields
		for (var i=0; i<inputs.length; i++){

			// easier reference to the element
			var this_input = inputs[i];


			// check the rel for proper limit indication
			var this_rel = this_input.get('rel');
			this_rel = this_rel.split('limit:');
			if (this_rel.length != 2)
				continue;
	
			// make sure there is nothing AFTER the limit amount
			this_rel = this_rel[1].split(' ');
			var this_limit = this_rel[0];

			// get a reference to the element's field help
			var this_help = $(this_input.get('id') + '_help');
			this_help.show();
			// determine how many chars are left in the limit with the current value
			var charsRemain = parseFloat(this_limit) - parseFloat(this_input.value.length);
			
			if (charsRemain < 0)
				var valString = '<span class="bold red">' + charsRemain + '</span>';
			else
				var valString = '<span class="green">' + charsRemain + '</span>';		
	
			var append_html = '';
			if (this_help.innerHTML.length > 0)
				append_html = '<br><br>'+this_help.innerHTML;
			
			this_help.innerHTML = 'Limit: ' + this_limit + '/Remaining: <span id="' + this_input.get("id") + '_limit">' + valString + '</span>'+append_html;
			this_input.addEvent('keyup', function () {
				Form.updateLimit(this);
				return false;
			});
		}
	},
	
	/**
	 * OnKeyUp event handler for form elements with maxlen=x and rel=limit
	 * Updates the counter for characters remaining
	 */
	updateLimit: function (targetElement) {
		var limitBox = $(targetElement.id + '_limit');
		if (!limitBox) {
		    return;
		}
		
		// check the rel for proper limit indication
		var this_rel = targetElement.get('rel');
		this_rel = this_rel.split('limit:');
		if (this_rel.length != 2)
			return;
	
		// make sure there is nothing AFTER the limit amount
		this_rel = this_rel[1].split(' ');
		var lenLimit = this_rel[0];
		var currLen = targetElement.value.length;
		var charsLeft = parseFloat(lenLimit) - parseFloat(currLen);
		if (charsLeft < 0) {
			$(targetElement).setStyle('background-color', '#FFEFEF');
			valString = '<span class="bold red">' + charsLeft + '</span>';
		} else {
			$(targetElement).setStyle('background-color', '');
			valString = '<span class="green">' + charsLeft + '</span>';
		}
		limitBox.innerHTML = valString;
		return;
	},
	
	
	set_hints: function () {
		// hide field hints
		$$('.field_hint').setStyles({
			float: '',
		    display: 'none',
		    position: 'relative',
		    width: '300px'
		});
		$$('.field_hint_text').setStyles({
		    border: '2px solid #ADAC9F',
		    backgroundColor: 'white'
		});
		$$('.field_hint_arrow').setStyle('display', 'block');
		var t = $$('.field_hint');
		
		// loop the hints, add the show/hide events
		for (var i=0; i<t.length; i++) {
		
			// grab a reference to the form node
			var formNode = t[i].getPrevious();
			
			
			// some containers have subcontainers, look out for those
			if (formNode.tagName != 'DIV') {
	
				// regular field, add the events to show/hide the hints
				t[i].getPrevious().addEvent('focus', function () {
					this.getNext().style.display = '';
					var ofHeight = (this.getNext().offsetHeight);
					ofHeight = ofHeight + this.getNext().getPrevious().offsetHeight;
					//console.log(ofHeight)
					this.getNext().style.marginTop = '-'+(ofHeight)+'px';
				})
				t[i].getPrevious().addEvent('blur', function () {
					this.getNext().style.display = 'none';	
				})			
			} else {		
		    	// check the next 6 parents, we're after the one with the
		    	// classname 'formElementContainer'
		    	find_parent = t[i].getParent();
		    	use_parent = false;
		    	for (var g=0;g<6;g++) {  		
					if (find_parent.get('class').indexOf('formElementContainer') != -1) {
						use_parent = find_parent;
						break;
					}	
					find_parent = $(find_parent.parentNode);					
				}
				
				// we were able to find the container, now we can target all form 
				// elements inside the container
				if (use_parent) {
				
					// grab a copy of the parent's id
					var up_id = use_parent.id;
					
					// give the hint an id so it can be referenced later
					t[i].id = up_id+'_hint';
					var ti_id = t[i].id; 
					$(ti_id).set('rel', up_id);
					
					// get all form nodes inside the parent
					var nodes = $$('#'+up_id+' input');
					
					// loop the nodes, add the events
					for (var z=0; z<nodes.length;z++) {
						// add the hint's id to the node's rel, so it can be
						// referenced later
						nodes.set('rel',ti_id);
						
						// add the events
						nodes[z].addEvent('focus', function () {
							$(this.get('rel')).style.display = '';
							var ofHeight = $(this.get('rel')).offsetHeight;
							ofHeight = $(this.get('rel')).getPrevious().offsetHeight + ofHeight + 10;
							$(this.get('rel')).style.marginTop = '-'+ofHeight+'px';
						})
						nodes[z].addEvent('blur', function () {
							$(this.get('rel')).style.display = 'none';	
						})
					} // end :: for looping nodes
				}// end :: if checking for parent
			} // end :: if checking for a element or a div
		}// end for :: looping all hints
	}
	
	
});
FormClass.implement(new Events, new Options);







var ___captcha_reloading = false;
function ___reload_captcha() {
	if (___captcha_reloading)
		return false;
		
	var captcha_key = $('__captcha_key').value;
	___captcha_reloading = true;
	$('___reload_captcha').innerHTML = 'Loading...';
	$('___captcha_wait').style.display = '';
	$('___captcha_image').src = $('___captcha_image').src + '&d=' + (new Date()).getTime();
	setTimeout("___release_reloading();", 1000)
	return false;
}
function ___release_reloading() {
	___captcha_reloading = false;
	$('___reload_captcha').innerHTML = 'Try a new one';
	$('___captcha_wait').style.display = 'none';
}


var ___formCityState = null;
CityStateUpdater = {
	element_groups : {},
	init : function (ele) {
		if (typeof ele == 'undefined')
			return false;
		if (this.initialized) {
			this.add_element(ele);
			return true;
		}
		this.verbose_errors = true;
		
		this.initialized = true;
		return this.add_element(ele);
	},
	
	add_element : function (ele) {
		if (ele.get('rel').indexOf('CityStateType:') != -1) {
			// parse the type out from the element's rel
			var regx = new RegExp('CityStateType:(.*)\\|(.*)\\| ');
  			var parms = regx.exec(ele.get('rel'));
  			
  			if (parms.length != 3)
  				return Form.CityStateUpdater.show_error('CityState::add_element() invalid CityState type or xref');


			if (parms[1] != 'state' && parms[1] != 'city' && parms[1] != 'zip')
				return Form.CityStateUpdater.show_error('CityState::add_element() invalid CityState type. Expecting "city", "state", or "zip", got "'+parms[1]+'"');

			if (parms[2].length == 0) {
				parms[2] = random_string(6)
				var new_ref_string = 'CityStateType:'+parms[1]+'|'+parms[2]+'|';
				var old_ref_string = 'CityStateType:'+parms[1]+'||';
				ele.set('rel', ele.get('rel').replace(old_ref_string, new_ref_string));								
			}

			if (typeof Form.CityStateUpdater.element_groups[parms[2]] == 'undefined') {
				Form.CityStateUpdater.element_groups[parms[2]] = {};
			}

			Form.CityStateUpdater.element_groups[parms[2]][parms[1]] = ele;
			ele.store('xref', parms[2]);
			ele.store('ref', Form.CityStateUpdater);
			// add the events
			if (parms[1] == 'city') {
				ele.addEvent('blur', function () {
					Form.CityStateUpdater.changed_city(this);
				});
				
				if (ele.get('value').length > 0)
					ele.store('current_value', ele.get('value'));

				var state_val = (typeof this.element_groups[parms[2]]['state'] != 'undefined') ? this.element_groups[parms[2]]['state'].get('value') : false;
				if (typeof this.element_groups[parms[2]]['state'] != 'undefined' && state_val) {
					jsonreq('___form_element_handler___', 'action=get_city_list&state='+this.element_groups[parms[2]]['state'].get('value')+'&xref='+parms[2], Form.CityStateUpdater.populate_cities);
				} else {
					if (ele.get('value').length > 0)
						jsonreq('___form_element_handler___', 'action=get_city_list&city='+ele.get('value')+'&xref='+parms[2], Form.CityStateUpdater.populate_cities);				
					else if (typeof this.element_groups[parms[2]]['state'] == 'undefined')
						jsonreq('___form_element_handler___', 'action=get_city_list&state=-1+&xref='+parms[2], Form.CityStateUpdater.populate_cities);
				}
				
			} else if (parms[1] == 'state') {
				ele.addEvent('blur', function () {
					Form.CityStateUpdater.changed_state(this);
				});
				$(ele).store('current_value', ele.get('value'));
				jsonreq('___form_element_handler___', 'action=get_state_list&xref='+parms[2], Form.CityStateUpdater.populate_states.bind(this));
			}

			return true;
		}

		return Form.CityStateUpdater.show_error('CityState::add_element() invalid element rel.');
	},
	show_error : function (err) {
		if (Form.CityStateUpdater.verbose_errors)
			show_error('This form contains errors, and may not function properly.<br><br>Error description: '+err);
		return false;
	},
	dump_elements : function () {
		Form.CityStateUpdater.show_errors(this.element_groups);
		return false;
	},

	changed_city : function (ele) {
		return true;
	},

	changed_state : function (ele) {
		if (!ele.get('value') || ele.get('value') == 0)
			return false;
		var xref = ele.retrieve('xref');
		// clear the zip
		if (typeof Form.CityStateUpdater.element_groups[xref]['zip'] != 'undefined') {
			Form.CityStateUpdater.element_groups[xref]['zip'].value = '';
		}
		// clear the cities, display loading message
		if (typeof Form.CityStateUpdater.element_groups[xref]['city'] != 'undefined') {
			Form.CityStateUpdater.element_groups[xref]['city'].options.length = 0;
			Form.CityStateUpdater.element_groups[xref]['city'].options[0] = new Option('loading...');
			jsonreq('___form_element_handler___', 'action=get_city_list&state='+ele.get('value')+'&xref='+xref, Form.CityStateUpdater.populate_cities)
		}		
		return true;
	},
	populate_cities : function (response) {
		//console.log('populate cities');
		if (typeof response.xref == 'undefined') {
			//console.log('no xref in populate cities');
			return false;
		}

		if (response.msg) {
			Form.CityStateUpdater.show_error(response.msg);
			return;
		}


		var current_value = Form.CityStateUpdater.element_groups[response.xref]['city'].retrieve('current_value');

		if (!$chk(current_value)) {
			current_value = response.default_value;
		}

		Form.CityStateUpdater.element_groups[response.xref]['city'].options.length = 0;
		
		// make sure it is visible
		Form.CityStateUpdater.element_groups[response.xref]['city'].show();
		
		var nOpt = new Option('select a city', '0');
		Form.CityStateUpdater.element_groups[response.xref]['city'].options[Form.CityStateUpdater.element_groups[response.xref]['city'].options.length] = nOpt;
		for (var y=0; y < response.list.length; y++) {
			var nOpt = new Option(response['list'][y].city, response['list'][y].city);
			$(nOpt).setAttribute('rel', response['list'][y].zip);
			Form.CityStateUpdater.element_groups[response.xref]['city'].options[y] = nOpt;
			if (current_value && current_value == response['list'][y].city)
				$(nOpt).set('selected', 'true');
		}
	
		return true;	
	},
	populate_states : function (response) {
		if (typeof response.xref == 'undefined')
			return false;

		if (response.msg) {
			Form.CityStateUpdater.show_errors(reponse.msg)
		}

		var using_default = false;
		var current_value = Form.CityStateUpdater.element_groups[response.xref]['state'].retrieve('current_value');

		if (current_value.length == 0) {
			current_value = response.default_value;
			using_default = true;
		}

		Form.CityStateUpdater.element_groups[response.xref]['state'].options.length = 0;
		
		var nOpt = new Option('select a state', '0');
		Form.CityStateUpdater.element_groups[response.xref]['state'].options[Form.CityStateUpdater.element_groups[response.xref]['state'].options.length] = nOpt;

		if (!Form.CityStateUpdater.element_groups[response.xref]['city'].retrieve('current_value')) {
			Form.CityStateUpdater.element_groups[response.xref]['city'].options.length = 0;
			var nOpt = new Option('select a state first', '0');
			Form.CityStateUpdater.element_groups[response.xref]['city'].options[Form.CityStateUpdater.element_groups[response.xref]['city'].options.length] = nOpt;
		}		
		for (var y=0; y < response.list.length; y++) {
			var nOpt = new Option(response['list'][y].state, response['list'][y].state);
			Form.CityStateUpdater.element_groups[response.xref]['state'].options[Form.CityStateUpdater.element_groups[response.xref]['state'].options.length] = nOpt;
			if (current_value && current_value == response['list'][y].state)
				$(nOpt).set('selected', 'true');
		}

		if (using_default == true) {
			jsonreq('___form_element_handler___', 'action=get_city_list&state='+current_value+'&xref='+response.xref, Form.CityStateUpdater.populate_cities.bind(this));
		}

		return true;	
	}
}



function form_tab_control (mode, targEle, fieldset_id) {
	if (typeof mode == 'undefined' || typeof targEle == 'undefined')
		return;	
	switch (mode) {
		case 'over':
			if (!$(targEle).hasClass('active'))
				$(targEle).addClass('hover');
			break;
		case 'out':
			if (!$(targEle).hasClass('active'))
				$(targEle).removeClass('hover');
			break;
		case 'click':
			___turn_off_all_form_tabs(fieldset_id);
			$(targEle).addClass('active');
			$('fieldset_'+fieldset_id).show();
			break;	
	}

}

function ___turn_off_all_form_tabs (fieldset_id) {
	var frm = $('fieldset_'+fieldset_id).form;
	var form_id = $(frm).get('id');
	var frm_tabs = $(frm).getPrevious('ul');
	var tds = $(frm_tabs).getChildren();
	tds.removeClass('hover');
	tds.removeClass('active');
	$(frm).getElements('fieldset').hide();
}



/*
Script: MooEditable.js
	Class for creating a WYSIWYG editor, for contentEditable-capable browsers.

License:
	MIT-style license.

Copyright:
	Copyright (c) 2007-2009 [Lim Chee Aun](http://cheeaun.com).
	
Build: %build%

Credits:
	- Code inspired by Stefan's work [Safari Supports Content Editing!](http://www.xs4all.nl/~hhijdra/stefan/ContentEditable.html) from [safari gets contentEditable](http://walkah.net/blog/walkah/safari-gets-contenteditable)
	- Main reference from Peter-Paul Koch's [execCommand compatibility](http://www.quirksmode.org/dom/execCommand.html)
	- Some ideas and code inspired by [TinyMCE](http://tinymce.moxiecode.com/)
	- Some functions inspired by Inviz's [Most tiny wysiwyg you ever seen](http://forum.mootools.net/viewtopic.php?id=746), [mooWyg (Most tiny WYSIWYG 2.0)](http://forum.mootools.net/viewtopic.php?id=5740)
	- Some regex from Cameron Adams's [widgEditor](http://widgeditor.googlecode.com/)
	- Some code from Juan M Martinez's [jwysiwyg](http://jwysiwyg.googlecode.com/)
	- Some reference from MoxieForge's [PunyMCE](http://punymce.googlecode.com/)
	- IE support referring Robert Bredlau's [Rich Text Editing](http://www.rbredlau.com/drupal/node/6)
	- Tango icons from the [Tango Desktop Project](http://tango.freedesktop.org/)
	- Additional Tango icons from Jimmacs' [Tango OpenOffice](http://www.gnome-look.org/content/show.php/Tango+OpenOffice?content=54799)
*/

var MooEditable = new Class({

	Implements: [Events, Options],

	options: {
		toolbar: true,
		cleanup: true,
		paragraphise: true,
		xhtml : true,
		semantics : true,
		actions: 'bold italic underline strikethrough | insertunorderedlist insertorderedlist indent outdent | undo redo | createlink unlink | urlimage | toggleview',
		handleSubmit: true,
		handleLabel: true,
		baseCSS: 'html{ height: 100%; cursor: text }\
			body{ font-family: Trebuchet MS, Verdana, Arial, sans-serif; border: 0; }',
		extraCSS: '',
		externalCSS: '',
		html: '<html>\
			<head>\
			<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\
			<style>{BASECSS} {EXTRACSS}</style>\
			{EXTERNALCSS}\
			</head>\
			<body>{CONTENT}</body>\
			</html>'
	},

	initialize: function(el, options){
		this.setOptions(options);
		this.textarea = $(el);
		this.textarea.store('MooEditable', this);
		this.actions = this.options.actions.clean().split(' ');
		this.keys = {};
		this.dialogs = {};
		this.actions.each(function(action){
			var act = MooEditable.Actions[action];
			if (!act) return;
			if (act.options){
				var key = act.options.shortcut;
				if (key) this.keys[key] = action;
			}
			if (act.dialogs){
				$each(act.dialogs, function(dialog, name){
					dialog = dialog.attempt(this);
					dialog.name = action + ':' + name;
					if ($type(this.dialogs[action]) != 'object') this.dialogs[action] = {};
					this.dialogs[action][name] = dialog;
				}, this);
			}
			if (act.events){
				$each(act.events, function(fn, event){
					this.addEvent(event, fn);
				}, this);
			}
		}.bind(this));
		this.render();
	},
	
	toElement: function(){
		return this.textarea;
	},
	
	render: function(){
		var self = this;
		
		// Dimensions
		var dimensions = this.textarea.getSize();

		// Build the container
		this.container = new Element('div', {
			id: (this.textarea.id) ? this.textarea.id + '-mooeditable-container' : null,
			'class': 'mooeditable-container',
			styles: {
				width: (dimensions.x > 0 ? dimensions.x : this.textarea.style.width) 
			}
		});


		// Override all textarea styles
		this.textarea.addClass('mooeditable-textarea').setStyle('height', (dimensions.y > 0 ? dimensions.y : this.textarea.style.height));

		// Build the iframe
		this.iframe = new IFrame({
			'class': 'mooeditable-iframe',
			styles: {
				height: (dimensions.y > 0 ? dimensions.y : this.textarea.style.height)
			}
		});
		
		this.toolbar = new MooEditable.UI.Toolbar({
			onItemAction: function(){
				var args = $splat(arguments);
				var item = args[0];
				self.action(item.name, args);
			}
		});
		this.attach();
		
		// Update the event for textarea's corresponding labels
		if (this.options.handleLabel && this.textarea.id) $$('label[for="'+this.textarea.id+'"]').addEvent('click', function(e){
			if (self.mode != 'iframe') return;
			e.preventDefault();
			self.focus();
		});

		// Update & cleanup content before submit
		if (this.options.handleSubmit){
			this.form = this.textarea.getParent('form');
			if (!this.form) return;
			this.form.addEvent('submit', function(){
				if (self.mode == 'iframe') self.saveContent();
			});
		}		
		this.fireEvent('render', this);
	},

	attach: function(){
		var self = this;

		// Assign view mode
		this.mode = 'iframe';
		
		// Editor iframe state
		this.editorDisabled = false;

		// Put textarea inside container
		this.container.wraps(this.textarea);

		this.textarea.setStyle('display', 'none');
		
		this.iframe.setStyle('display', '').inject(this.textarea, 'before');
		
		$each(this.dialogs, function(action, name){
			$each(action, function(dialog){
				$(dialog).inject(self.iframe, 'before');
				var range;
				dialog.addEvents({
					open: function(){
						range = self.selection.getRange();
						self.editorDisabled = true;
						self.toolbar.disable(name);
						self.fireEvent('dialogOpen', this);
					},
					close: function(){
						self.toolbar.enable();
						self.editorDisabled = false;
						self.focus();
						if (range) self.selection.setRange(range);
						self.fireEvent('dialogClose', this);
					}
				});
			});
		});

		// contentWindow and document references
		this.win = this.iframe.contentWindow;
		this.doc = this.win.document;

		// Build the content of iframe
		var docHTML = this.options.html.substitute({
			BASECSS: this.options.baseCSS,
			EXTRACSS: this.options.extraCSS,
			EXTERNALCSS: (this.options.externalCSS) ? '<link rel="stylesheet" href="' + this.options.externalCSS + '">': '',
			CONTENT: this.cleanup(this.textarea.get('value'))
		});
		this.doc.open();
		this.doc.write(docHTML);
		this.doc.close();

		// Turn on Design Mode
		// IE fired load event twice if designMode is set
		(Browser.Engine.trident) ? this.doc.body.contentEditable = true : this.doc.designMode = 'On';

		// Mootoolize window, document and body
		if (!this.win.$family) new Window(this.win);
		if (!this.doc.$family) new Document(this.doc);
		$(this.doc.body);

		// Bind keyboard shortcuts
		this.doc.addEvents({
			mouseup: this.editorMouseUp.bind(this),
			mousedown: this.editorMouseDown.bind(this),
			contextmenu: this.editorContextMenu.bind(this),
			click: this.editorClick.bind(this),
			dbllick: this.editorDoubleClick.bind(this),
			keypress: this.editorKeyPress.bind(this),
			keyup: this.editorKeyUp.bind(this),
			keydown: this.editorKeyDown.bind(this)
		});
		this.textarea.addEvent('keypress', this.textarea.retrieve('mooeditable:textareaKeyListener', this.keyListener.bind(this)));
		
		// Fix window focus event not firing on Firefox 2
		if (Browser.Engine.gecko && Browser.Engine.version == 18) this.doc.addEvent('focus', function(){
			self.win.fireEvent('focus').focus();
		});

		// styleWithCSS, not supported in IE and Opera
		if (!(/trident|presto/i).test(Browser.Engine.name)){
			var styleCSS = function(){
				self.execute('styleWithCSS', false, false);
				self.doc.removeEvent('focus', styleCSS);
			};
			this.win.addEvent('focus', styleCSS);
		}

		if (this.options.toolbar){
			$(this.toolbar).inject(this.container, 'top');
			this.toolbar.render(this.actions);
		}

		this.selection = new MooEditable.Selection(this.win);
		
		this.fireEvent('attach', this);
		
		return this;
	},
	
	detach: function(){
		this.saveContent();
		this.textarea.setStyle('display', '').removeClass('mooeditable-textarea').inject(this.container, 'before');
		this.textarea.removeEvent('keypress', this.textarea.retrieve('mooeditable:textareaKeyListener'));
		this.container.dispose();
		this.fireEvent('detach', this);
		return this;
	},
	
	editorMouseUp: function(e){
		if (this.editorDisabled){
			e.stop();
			return;
		}
		
		if (this.options.toolbar) this.checkStates();
		
		this.fireEvent('editorMouseUp', e);
	},
	
	editorMouseDown: function(e){
		if (this.editorDisabled){
			e.stop();
			return;
		}
		
		this.fireEvent('editorMouseDown', e);
	},
	
	editorContextMenu: function(e){
		if (this.editorDisabled){
			e.stop();
			return;
		}
		
		this.fireEvent('editorContextMenu', e);
	},
	
	editorClick: function(e){
		// make images selectable and draggable in Safari
		if (Browser.Engine.webkit){
			var el = e.target;
			if (el.get('tag') == 'img'){
				this.selection.selectNode(el);
			}
		}
		
		this.fireEvent('editorClick', e);
	},
	
	editorDoubleClick: function(e){
		this.fireEvent('editorDoubleClick', e);
	},
	
	editorKeyPress: function(e){
		if (this.editorDisabled){
			e.stop();
			return;
		}
		
		this.keyListener(e);
		
		this.fireEvent('editorKeyPress', e);
	},
	
	editorKeyUp: function(e){
		if (this.editorDisabled){
			e.stop();
			return;
		}
		
		if (this.options.toolbar) this.checkStates();
		
		this.fireEvent('editorKeyUp', e);
	},
	
	editorKeyDown: function(e){
		if (this.editorDisabled){
			e.stop();
			return;
		}
		
		if (e.key == 'enter'){
			if (this.options.paragraphise) {
				if (e.shift && Browser.Engine.webkit){
					var s = this.selection;
					var r = s.getRange();
					// Insert BR element
					var br = this.doc.createElement('br');
					r.insertNode(br);

					// Place caret after BR
					r.setStartAfter(br);
					r.setEndAfter(br);
					s.setRange(r);
					
					// Could not place caret after BR then insert an nbsp entity and move the caret
					if (s.getSelection().focusNode == br.previousSibling){
						var nbsp = this.doc.createTextNode('\u00a0');
						var p = br.parentNode;
						var ns = br.nextSibling;
						(ns) ? p.insertBefore(nbsp, ns) : p.appendChild(nbsp);
						s.selectNode(nbsp);
						s.collapse(1);
					}
					
					// Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117
					this.win.scrollTo(0, Element.getOffsets(s.getRange().startContainer).y);
					
					e.preventDefault();
				} else if (Browser.Engine.gecko || Browser.Engine.webkit){
					var node = this.selection.getNode();
					var blockEls = /^(H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD)$/;
					var isBlock = node.getParents().include(node).some(function(el){
						return el.nodeName.test(blockEls);
					});
					if (!isBlock) this.execute('insertparagraph');
				} else if (!e.shift && !Browser.Engine.webkit && !Browser.Engine.gecko) {
					this.execute('insertparagraph');
				}
			} else {
				if (Browser.Engine.trident){
					var r = this.selection.getRange();
					var node = this.selection.getNode();
					if (r && node.get('tag') != 'li'){
						this.selection.insertContent('<br>');
						this.selection.collapse(false);
					}
					e.preventDefault();
				}
			}
		}
		
		this.fireEvent('editorKeyDown', e);
	},
	
	keyListener: function(e){
		var key = (Browser.Platform.mac) ? e.meta : e.control;
		if (!key || !this.keys[e.key]) return;
		e.preventDefault();
		var item = this.toolbar.getItem(this.keys[e.key]);
		item.action(e);
	},

	focus: function(){
		// needs the delay to get focus working
		(function(){ 
			(this.mode == 'iframe' ? this.win : this.textarea).focus();
			this.fireEvent('focus', this);
		}).bind(this).delay(10);
		return this;
	},

	action: function(command, args){
		var action = MooEditable.Actions[command];
		if (action.command && $type(action.command) == 'function'){
			action.command.run(args, this);
		} else {
			this.focus();
			this.execute(command, false, args);
			if (this.mode == 'iframe') this.checkStates();
		}
	},

	execute: function(command, param1, param2){
		if (this.busy) return;
		this.busy = true;
		this.doc.execCommand(command, param1, param2);
		this.saveContent();
		this.busy = false;
		return false;
	},

	toggleView: function(){
		this.fireEvent('beforeToggleView', this);
		if (this.mode == 'textarea'){
			this.mode = 'iframe';
			this.iframe.setStyle('display', '');
			this.setContent(this.textarea.value);
			this.textarea.setStyle('display', 'none');
		} else {
			this.saveContent();
			this.mode = 'textarea';
			this.textarea.setStyle('display', '');
			this.iframe.setStyle('display', 'none');
		}
		this.fireEvent('toggleView', this);
		this.focus();
		return this;
	},

	getContent: function(){
		return this.cleanup(this.doc.body.get('html'));
	},

	setContent: function(newContent){
		this.doc.body.set('html', newContent);
		return this;
	},

	saveContent: function(){
		if (this.mode == 'iframe') this.textarea.set('value', this.getContent());
		return this;
	},

	checkStates: function(){
		this.actions.each(function(action){
			var item = this.toolbar.getItem(action);
			if (!item) return;
			item.deactivate();

			var states = MooEditable.Actions[action]['states'];
			if (!states) return;
			
			var el = this.selection.getNode();
			if (!el) return;
			
			// custom checkState
			if ($type(states) == 'function'){
				states.attempt(el, item);
				return;
			}
			
			try{
				if (this.doc.queryCommandState(action)){
					item.activate();
					return;
				}
			} catch(e) {}
			
			if (states.tags){
				do {
					if ($type(el) != 'element') break;
					var tag = el.tagName.toLowerCase();
					if (states.tags.contains(tag)){
						item.activate(tag);
						break;
					}
				}
				while (el = el.parentNode);
			}

			if (states.css){
				var blockEls = /^(H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD)$/;
				do {
					if ($type(el) != 'element') break;
					var found = false;
					for (var prop in states.css){
						var css = states.css[prop];
						if ($(el).getStyle(prop).contains(css)){
							item.activate(css);
							found = true;
						}
					}
					if (found || el.tagName.test(blockEls)) break;
				}
				while (el = el.parentNode);
			}
		}.bind(this));
	},

	cleanup: function(source){
		if (!this.options.cleanup) return source.trim();
		
		if (typeof source == 'undefined')
			return '';
		
		do {
			var oSource = source;

			// Webkit cleanup
			source = source.replace(/<br class\="webkit-block-placeholder">/gi, "<br />");
			source = source.replace(/<span class="Apple-style-span">(.*)<\/span>/gi, '$1');
			source = source.replace(/ class="Apple-style-span"/gi, '');
			source = source.replace(/<span style="">/gi, '');

			// Remove padded paragraphs
			source = source.replace(/<p>\s*<br ?\/?>\s*<\/p>/gi, '<p>\u00a0</p>');
			source = source.replace(/<p>(&nbsp;|\s)*<\/p>/gi, '<p>\u00a0</p>');
			if (!this.options.semantics){
				source = source.replace(/\s*<br ?\/?>\s*<\/p>/gi, '</p>');
			}

			// Replace improper BRs (only if XHTML : true)
			if (this.options.xhtml){
				source = source.replace(/<br>/gi, "<br />");
			}

			if (this.options.semantics){
				//remove divs from <li>
				if (Browser.Engine.trident){
					source = source.replace(/<li>\s*<div>(.+?)<\/div><\/li>/g, '<li>$1</li>');
				}
				//remove stupid apple divs
				if (Browser.Engine.webkit){
					source = source.replace(/^([\w\s]+.*?)<div>/i, '<p>$1</p><div>');
					source = source.replace(/<div>(.+?)<\/div>/ig, '<p>$1</p>');
				}

				//<p> tags around a list will get moved to after the list
				if (['gecko', 'presto', 'webkit'].contains(Browser.Engine.name)){
					//not working properly in safari?
					source = source.replace(/<p>[\s\n]*(<(?:ul|ol)>.*?<\/(?:ul|ol)>)(.*?)<\/p>/ig, '$1<p>$2</p>');
					source = source.replace(/<\/(ol|ul)>\s*(?!<(?:p|ol|ul|img).*?>)((?:<[^>]*>)?\w.*)$/g, '</$1><p>$2</p>');
				}

				source = source.replace(/<br[^>]*><\/p>/g, '</p>');			//remove <br>'s that end a paragraph here.
				source = source.replace(/<p>\s*(<img[^>]+>)\s*<\/p>/ig, '$1\n'); 	//if a <p> only contains <img>, remove the <p> tags

				//format the source
				source = source.replace(/<p([^>]*)>(.*?)<\/p>(?!\n)/g, '<p$1>$2</p>\n');  	//break after paragraphs
				source = source.replace(/<\/(ul|ol|p)>(?!\n)/g, '</$1>\n'); 			//break after </p></ol></ul> tags
				source = source.replace(/><li>/g, '>\n\t<li>'); 				//break and indent <li>
				source = source.replace(/([^\n])<\/(ol|ul)>/g, '$1\n</$2>');  			//break before </ol></ul> tags
				source = source.replace(/([^\n])<img/ig, '$1\n<img'); 				//move images to their own line
				source = source.replace(/^\s*$/g, '');						//delete empty lines in the source code (not working in opera)
			}

			// Remove leading and trailing BRs
			source = source.replace(/<br ?\/?>$/gi, '');
			source = source.replace(/^<br ?\/?>/gi, '');

			// Remove useless BRs
			source = source.replace(/><br ?\/?>/gi, '>');

			// Remove BRs right before the end of blocks
			source = source.replace(/<br ?\/?>\s*<\/(h1|h2|h3|h4|h5|h6|li|p)/gi, '</$1');

			// Semantic conversion
			source = source.replace(/<span style="font-weight: bold;">(.*)<\/span>/gi, '<strong>$1</strong>');
			source = source.replace(/<span style="font-style: italic;">(.*)<\/span>/gi, '<em>$1</em>');
			source = source.replace(/<b\b[^>]*>(.*?)<\/b[^>]*>/gi, '<strong>$1</strong>');
			source = source.replace(/<i\b[^>]*>(.*?)<\/i[^>]*>/gi, '<em>$1</em>');
			source = source.replace(/<u\b[^>]*>(.*?)<\/u[^>]*>/gi, '<span style="text-decoration: underline;">$1</span>');

			// Replace uppercase element names with lowercase
			source = source.replace(/<[^> ]*/g, function(match){return match.toLowerCase();});

			// Replace uppercase attribute names with lowercase
			source = source.replace(/<[^>]*>/g, function(match){
				   match = match.replace(/ [^=]+=/g, function(match2){return match2.toLowerCase();});
				   return match;
			});

			// Put quotes around unquoted attributes
			source = source.replace(/<[^>]*>/g, function(match){
				   match = match.replace(/( [^=]+=)([^"][^ >]*)/g, "$1\"$2\"");
				   return match;
			});

			//make img tags xhtml compatable
			//           if (this.options.xhtml){
			//                source = source.replace(/(<(?:img|input)[^/>]*)>/g, '$1 />');
			//           }

			//remove double <p> tags and empty <p> tags
			source = source.replace(/<p>(?:\s*)<p>/g, '<p>');
			source = source.replace(/<\/p>\s*<\/p>/g, '</p>');
			source = source.replace(/<p>\W*<\/p>/g, '');

			// Final trim
			source = source.trim();
		}
		while (source != oSource);

		return source;
	}

});

MooEditable.Selection = new Class({

	initialize: function(win){
		this.win = win;
	},

	getSelection: function(){
		this.win.focus();
		return (this.win.getSelection) ? this.win.getSelection() : this.win.document.selection;
	},

	getRange: function(){
		var s = this.getSelection();

		if (!s) return null;

		try {
			return s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : null);
		} catch(e) {
			// IE bug when used in frameset
			return this.doc.body.createTextRange();
		}
	},

	setRange: function(range){
		if (range.select){
			$try(function(){
				range.select();
			});
		} else {
			var s = this.getSelection();
			if (s.addRange){
				s.removeAllRanges();
				s.addRange(range);
			}
		}
	},

	selectNode: function(node, collapse){
		var r = this.getRange();
		var s = this.getSelection();

		if (r.moveToElementText){
			$try(function(){
				r.moveToElementText(node);
				r.select();
			});
		} else if (s.addRange){
			collapse ? r.selectNodeContents(node) : r.selectNode(node);
			s.removeAllRanges();
			s.addRange(r);
		} else {
			s.setBaseAndExtent(node, 0, node, 1);
		}

		return node;
	},

	isCollapsed: function(){
		var r = this.getRange();
		if (r.item) return false;
		return r.boundingWidth == 0 || this.getSelection().isCollapsed;
	},

	collapse: function(toStart){
		var r = this.getRange();
		var s = this.getSelection();

		if (r.select){
			r.collapse(toStart);
			r.select();
		} else {
			toStart ? s.collapseToStart() : s.collapseToEnd();
		}
	},

	getContent: function(){
		var r = this.getRange();
		var body = new Element('body');

		if (this.isCollapsed()) return '';

		if (r.cloneContents){
			body.appendChild(r.cloneContents());
		} else if ($defined(r.item) || $defined(r.htmlText)){
			body.set('html', r.item ? r.item(0).outerHTML : r.htmlText);
		} else {
			body.set('html', r.toString());
		}

		var content = body.get('html');
		return content;
	},

	getText : function(){
		var r = this.getRange();
		var s = this.getSelection();

		return this.isCollapsed() ? '' : r.text || s.toString();
	},

	getNode: function(){
		var r = this.getRange();

		if (!Browser.Engine.trident){
			var el = null;

			if (r){
				el = r.commonAncestorContainer;

				// Handle selection a image or other control like element such as anchors
				if (!r.collapsed)
					if (r.startContainer == r.endContainer)
						if (r.startOffset - r.endOffset < 2)
							if (r.startContainer.hasChildNodes())
								el = r.startContainer.childNodes[r.startOffset];

				while ($type(el) != 'element') el = el.parentNode;
			}

			return $(el);
		}

		return $(r.item ? r.item(0) : r.parentElement());
	},

	insertContent: function(content){
		if (Browser.Engine.trident){
			var r = this.getRange();
			r.pasteHTML(content);
			r.collapse(false);
			r.select();
		} else {
			this.win.document.execCommand('insertHTML', false, content);
		}
	}

});

MooEditable.UI = {};

MooEditable.UI.Toolbar= new Class({

	Implements: [Events, Options],

	options: {
		/*
		onItemAction: $empty,
		*/
		'class': ''
	},
    
	initialize: function(options){
		this.setOptions(options);
		this.el = new Element('div', {'class': 'mooeditable-ui-toolbar ' + this.options['class']});
		this.items = {};
		this.content = null;
	},
	
	toElement: function(){
		return this.el;
	},
	
	render: function(actions){
		if (this.content){
			this.el.adopt(this.content);
		} else {
			this.content = actions.map(function(action){
				return (action == '|') ? this.addSeparator() : this.addItem(action);
			}.bind(this));
		}
		return this;
	},
	
	addItem: function(action){
		var self = this;
		var act = MooEditable.Actions[action];
		if (!act) return;
		var type = act.type || 'button';
		var options = act.options || {};
		var item = new MooEditable.UI[type.camelCase().capitalize()]($extend(options, {
			name: action,
			'class': action + '-item toolbar-item',
			title: act.title,
			onAction: self.itemAction.bind(self)
		}));
		this.items[action] = item;
		$(item).inject(this.el);
		return item;
	},
	
	getItem: function(action){
		return this.items[action];
	},
	
	addSeparator: function(){
		return new Element('span', {'class': 'toolbar-separator'}).inject(this.el);
	},
	
	itemAction: function(){
		this.fireEvent('itemAction', arguments);
	},

	disable: function(except){
		$each(this.items, function(item){
			(item.name == except) ? item.activate() : item.deactivate().disable();
		});
		return this;
	},

	enable: function(){
		$each(this.items, function(item){
			item.enable();
		});
		return this;
	},
	
	show: function(){
		this.el.setStyle('display', '');
		return this;
	},
	
	hide: function(){
		this.el.setStyle('display', 'none');
		return this;
	}
	
});

MooEditable.UI.Button = new Class({

	Implements: [Events, Options],

	options: {
		/*
		onAction: $empty,
		*/
		title: '',
		name: '',
		text: 'Button',
		'class': '',
		shortcut: '',
		mode: 'icon'
	},

	initialize: function(options){
		this.setOptions(options);
		this.name = this.options.name;
		this.render();
	},
	
	toElement: function(){
		return this.el;
	},
	
	render: function(){
		var self = this;
		var key = (Browser.Platform.mac) ? 'Cmd' : 'Ctrl';
		var shortcut = (this.options.shortcut) ? ' ( ' + key + '+' + this.options.shortcut.toUpperCase() + ' )' : '';
		var text = this.options.title || name;
		var title = text + shortcut;
		this.el = new Element('button', {
			'class': 'mooeditable-ui-button ' + self.options['class'],
			title: title,
			html: '<span class="button-icon"></span><span class="button-text">' + text + '</span>',
			events: {
				click: self.click.bind(self),
				mousedown: function(e){ e.preventDefault(); }
			}
		});
		if (this.options.mode != 'icon') this.el.addClass('mooeditable-ui-button-' + this.options.mode);
		
		this.active = false;
		this.disabled = false;

		// add hover effect for IE
		if (Browser.Engine.trident) this.el.addEvents({
			mouseenter: function(e){ this.addClass('hover'); },
			mouseleave: function(e){ this.removeClass('hover'); }
		});
		
		return this;
	},
	
	click: function(e){
		e.preventDefault();
		if (this.disabled) return;
		this.action(e);
	},
	
	action: function(){
		this.fireEvent('action', [this].concat($A(arguments)));
	},
	
	enable: function(){
		if (this.active) this.el.removeClass('onActive');
		if (!this.disabled) return;
		this.disabled = false;
		this.el.removeClass('disabled').set({
			disabled: false,
			opacity: 1
		});
		return this;
	},
	
	disable: function(){
		if (this.disabled) return;
		this.disabled = true;
		this.el.addClass('disabled').set({
			disabled: true,
			opacity: 0.4
		});
		return this;
	},
	
	activate: function(){
		if (this.disabled) return;
		this.active = true;
		this.el.addClass('onActive');
		return this;
	},
	
	deactivate: function(){
		this.active = false;
		this.el.removeClass('onActive');
		return this;
	}
	
});

MooEditable.UI.Dialog = new Class({

	Implements: [Events, Options],

	options:{
		/*
		onOpen: $empty,
		onClose: $empty,
		*/
		'class': '',
		contentClass: ''
	},

	initialize: function(html, options){
		this.setOptions(options);
		this.html = html;
		
		var self = this;
		this.el = new Element('div', {
			'class': 'mooeditable-ui-dialog ' + self.options['class'],
			html: '<div class="dialog-content ' + self.options.contentClass + '">' + html + '</div>',
			styles: {
				'display': 'none'
			},
			events: {
				click: self.click.bind(self)
			}
		});
	},
	
	toElement: function(){
		return this.el;
	},
	
	click: function(){
		this.fireEvent('click', arguments);
		return this;
	},
	
	open: function(){
		this.el.setStyle('display', '');
		this.fireEvent('open', this);
		return this;
	},
	
	close: function(){
		this.el.setStyle('display', 'none');
		this.fireEvent('close', this);
		return this;
	}

});

MooEditable.UI.AlertDialog = function(alertText){
	if (!alertText) return;
	var html = alertText + ' <button class="dialog-ok-button">OK</button>';
	return new MooEditable.UI.Dialog(html, {
		'class': 'mooeditable-alert-dialog',
		onOpen: function(){
			var button = this.el.getElement('.dialog-ok-button');
			(function(){
				button.focus();
			}).delay(10);
		},
		onClick: function(e){
			e.preventDefault();
			if (e.target.tagName.toLowerCase() != 'button') return;
			if ($(e.target).hasClass('dialog-ok-button')) this.close();
		}
	});
};

MooEditable.UI.PromptDialog = function(questionText, answerText, fn){
	if (!questionText) return;
	var html = '<label class="dialog-label">' + questionText
		+ ' <input type="text" class="text dialog-input" value="' + answerText + '">'
		+ '</label> <button class="dialog-button dialog-ok-button">OK</button>'
		+ '<button class="dialog-button dialog-cancel-button">Cancel</button>';
	return new MooEditable.UI.Dialog(html, {
		'class': 'mooeditable-prompt-dialog',
		onOpen: function(){
			var input = this.el.getElement('.dialog-input');
			(function(){
				input.focus()
				input.select();
			}).delay(10);
		},
		onClick: function(e){
			e.preventDefault();
			if (e.target.tagName.toLowerCase() != 'button') return;
			var button = $(e.target);
			var input = this.el.getElement('.dialog-input');
			if (button.hasClass('dialog-cancel-button')){
				input.set('value', answerText);
				this.close();
			} else if (button.hasClass('dialog-ok-button')){
				var answer = input.get('value');
				input.set('value', answerText);
				this.close();
				if (fn) fn.attempt(answer, this);
			}
		}
	});
};

MooEditable.Actions = new Hash({

	bold: {
		title: 'Bold',
		options: {
			shortcut: 'b'
		},
		states: {
			tags: ['b', 'strong'],
			css: {'font-weight': 'bold'}
		}
	},
	
	italic: {
		title: 'Italic',
		options: {
			shortcut: 'i'
		},
		states: {
			tags: ['i', 'em'],
			css: {'font-style': 'italic'}
		}
	},
	
	underline: {
		title: 'Underline',
		options: {
			shortcut: 'u'
		},
		states: {
			tags: ['u'],
			css: {'text-decoration': 'underline'}
		}
	},
	
	strikethrough: {
		title: 'Strikethrough',
		options: {
			shortcut: 's'
		},
		states: {
			tags: ['s', 'strike'],
			css: {'text-decoration': 'line-through'}
		}
	},
	
	insertunorderedlist: {
		title: 'Unordered List',
		states: {
			tags: ['ul']
		}
	},
	
	insertorderedlist: {
		title: 'Ordered List',
		states: {
			tags: ['ol']
		}
	},
	
	indent: {
		title: 'Indent',
		states: {
			tags: ['blockquote']
		}
	},
	
	outdent: {
		title: 'Outdent'
	},
	
	undo: {
		title: 'Undo',
		options: {
			shortcut: 'z'
		}
	},
	
	redo: {
		title: 'Redo',
		options: {
			shortcut: 'y'
		}
	},
	
	unlink: {
		title: 'Remove Hyperlink'
	},

	createlink: {
		title: 'Add Hyperlink',
		options: {
			shortcut: 'l'
		},
		states: {
			tags: ['a']
		},
		dialogs: {
			alert: MooEditable.UI.AlertDialog.pass('Please select the text you wish to hyperlink.'),
			prompt: function(editor){
				return MooEditable.UI.PromptDialog('Enter URL', 'http://', function(url){
					editor.execute('createlink', false, url.trim());
				});
			}
		},
		command: function(){
			if (this.selection.isCollapsed()){
				this.dialogs.createlink.alert.open();
			} else {
				var text = this.selection.getText();
				var url = /^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i;
				var prompt = this.dialogs.createlink.prompt;
				if (url.test(text)) prompt.el.getElement('.mooeditable-dialog-input').set('value', text);
				prompt.open();
			}
		}
	},

	urlimage: {
		title: 'Add Image',
		options: {
			shortcut: 'm'
		},
		dialogs: {
			prompt: function(editor){
				return MooEditable.UI.PromptDialog('Enter image URL', 'http://', function(url){
					editor.execute("insertimage", false, url.trim());
				});
			}
		},
		command: function(){
			this.dialogs.urlimage.prompt.open();
		}
	},

	toggleview: {
		title: 'Toggle View',
		command: function(){
			(this.mode == 'textarea') ? this.toolbar.enable() : this.toolbar.disable('toggleview');
			this.toggleView();
		}
	}

});

Element.Properties.mooeditable = {

	set: function(options){
		return this.eliminate('mooeditable').store('mooeditable:options', options);
	},

	get: function(options){
		if (options || !this.retrieve('mooeditable')){
			if (options || !this.retrieve('mooeditable:options')) this.set('mooeditable', options);
			this.store('mooeditable', new MooEditable(this, this.retrieve('mooeditable:options')));
		}
		return this.retrieve('mooeditable');
	}

};

Element.implement({

	mooEditable: function(options){
		return this.get('mooeditable', options);
	}

});



var UserPickerObject = new Class({	
	getOptions: function(){
		return {
			'target' : null,
			'userdata' : '',
			'change' : false,
			'clear' : false,
			'current_value' : false
		};
	},
	
	initialize: function(options){
		this.options = options;
		var up = $$('.USER_PICKER');
		//console.log('UP')
		//console.log(up)
	
		this.picker_open = false;
		this.preview_open = false;

		this.ref = options.ref;
		
		// parse the user data
		var raw_userdata = options.userdata.split(',');
		this.userdata = {};		
		$each(raw_userdata, function(this_user, index) {
			var userdata_parts = this_user.split('|');
			this.userdata[userdata_parts[0]] = userdata_parts;
		}, this);

		// grab the data target
		this.data_target = $(options.target);

		// create the display target
		var display_container = new Element('div').inject(this.data_target, 'after');
		var display_icon = new Element('img', {
			'src' : URL+'site/img/icons/user.png',
			'style' : 'float:left;'
		}).inject(display_container);
		this.display_target = new Element('div', {
			'styles' : {
				'margin-left' : '4px',
				'float' : 'left',
				'text-align': 'left'
			},
			'class' : 'mediumText darkblue bold'
		}).inject(display_container);
		if (options.current_value > 0) {
			this.display_target.set('html', this.userdata[options.current_value][1] + ' ' + this.userdata[options.current_value][2]);
		} else {
			this.display_target.set('html', '(no user selected)'); 
		}


		// add the control buttons
		if (options.change == true) {
			var change_link = new Element('a', {
				'class' : 'bottom_list_control user_picker_control',
				'style' : 'float:left;padding-top:0px;margin-top:0px'
			}).inject(display_container).addEvent('click', this.change.bind(this));
			var change_icon = new Element('img', {
				'src' : URL+'site/img/icons/check.png', 
				'class' : 'user_picker_control'
			}).inject(change_link).addEvent('click', function () {this.getParent().fireEvent('click')});
			var change_text = new Element('span', {
				'html' : ' select user', 
				'class' : 'user_picker_control'
			}).inject(change_link).addEvent('click', function () {this.getParent().fireEvent('click')});
		}
		if (options.clear == true) {
			var clear_link = new Element('a', {
				'class' : 'bottom_list_control user_picker_control',
				'style' : 'float:left;padding-top:0px;margin-top:0px'
			}).inject(display_container).addEvent('click', this.clear.bind(this));
			var change_icon = new Element('img', {
				'src' : URL+'site/img/icons/clear.png', 
				'class' : 'user_picker_control'
			}).inject(clear_link).addEvent('click', function () {this.getParent().fireEvent('click')});
			var change_text = new Element('span', {
				'html' : ' clear', 
				'class' : 'user_picker_control'
			}).inject(clear_link).addEvent('click', function () {this.getParent().fireEvent('click')});			
		}

		// set the data target as hidden
		this.data_target.set('type', 'hidden');
		this.data_target.set('rel', this.ref);

		document.addEvent('mousedown', this.close.bind(this));

		return this;
	},
	

	change : function () {
		if (this.preview_open == true || this.picker_open == true)
			return;

		// flag the picker as open	
		this.preview_open = false;
		this.picker_open = true;

		// grab the position of the target element
		var tPos = this.display_target.getPosition();
		
		// build the picker dialog
		this.container = new Element ('div', {
			'styles' : {
				'background-image' : 'url(site/img/forms/wide_frame.png)',
				'background-repeat' : 'no-repeat',
				'position' : 'absolute',
				'top' : tPos.y,
				'left' : tPos.x,
				'width' : '342px',
				'height' : '250px',
				'opacity' : '0.0',
				'text-align' : 'left',
				'padding-top' : '6px',
				'padding-left' : '8px',
				'overflow' : 'hidden'
			}, 
			'id' : 'user_picker_dialog',
			'class' : 'user_picker_control'
		}).inject($('body'));
		
		var heading = new Element('div', {
			'class' : 'mediumText white bold user_picker_control',
			'html' : 'Select a user'
		}).inject(this.container);
		
		
		var filter_label = new Element('div', {
			'class' : 'mediumText black bold user_picker_control',
			'html' : 'Type a name, address, or email address to filter',
			'styles' : {
				'margin-top' : '12px'
			}
		}).inject(this.container);
		var search_box = new Element('input', {
			'type' : 'textfield',
			'styles' : {
				'float' : 'none',
				'width' : '250px',
				'margin' : 'auto'
			},
			'class' : 'user_picker_control'
		}).inject(this.container).addEvent('keydown', this.parse_filter.bind(this))
		search_box.addEvent('click', this.check_preview.bind(this))

		var results_heading = new Element('div', {
			'class' : 'mediumText black bold user_picker_control',
			'html' : 'Results',
			'styles' : {
				'margin-top' : '8px',
				'border-bottom' : '1px solid black'
			}
		}).inject(this.container);		
		this.results_container = new Element('div', {
			'class' : 'mediumText black bold user_picker_control',
			'styles' : {
				'margin-top' : '8px',
				'height' : '146px',
				'overflow' : 'auto',
				'float' : 'left',
				'width' : '340px'
			},
			'html' : 'Type 3 more characters to begin filtering'
		}).inject(this.container);

		this.container.fade('in');
		return;
	},
	
	
	parse_filter : function (e) {
		//console.log(this)
		//console.log('picker open:'+this.picker_open);
		//console.log(this.ref)
		if (this.picker_open != true || this.preview_open == true)
			return;
		//console.log('working...')
		// get the target filter textbox
		var filter_ele = e.target;
		//console.log(filter_ele)
		// determine if this keystroke changes the length of the filter term
		var new_length = filter_ele.value.length;
		if (e.key == 'backspace')
			new_length--;
		else if ((e.code > 64 && e.code < 91) || e.code == 32)
			new_length++;
		else
			return true;

		if (new_length < 0)
			new_length = 0;
		
		// the input isn't long enough, ask for more
		if (new_length < 3) {	
			html = 'Type ' + (3 - new_length) + ' more characters to begin filtering';
			this.results_container.set('html', html);
			return true;
		}
		
		// clear the results container
		this.results_container.set('html', '');
		
		// determine what the lookup field is
		lookup_string = filter_ele.value.toLowerCase()
		lup = lookup_string.split(' ');			
		if (lup.length > 1) {
			if (IsNumeric(lup[0])) {
				// searching address
				search_type = 'address';
			} else {
				// searching first and last name
				search_type = 'name';
			}
		}
		if (IsNumeric(lookup_string)) {
			search_type = 'address';
		}
		lup = lookup_string.split('@');			
		if (lup.length > 1) {
			// searching email
			search_type = 'email';
		}
		
		if (typeof search_type == 'undefined') {
			search_type = 'both';
		}

		// loop the results, show 10, overflow the rest with a message
		add_cnt = 0;
		overflow_cnt = 0;
		for (index in this.userdata) {
			this_user = this.userdata[index];
			// make sure we've got enough data to list it
			if (typeof this_user[0] == 'undefined' || typeof this_user[1] == 'undefined' || typeof this_user[2] == 'undefined' || typeof this_user[3] == 'undefined' || typeof this_user[4] == 'undefined')
				continue;

			// add me flag determines if this is added to the results
			add_me = false;
			switch (search_type) {
				default:
				case 'both':
					term = this_user[1].toLowerCase() + ' ' + this_user[2].toLowerCase() + ' ' + this_user[3].toLowerCase();
					if (term.search(lookup_string) > -1) {
						add_me = true;
					}
					break;
				case 'name':
					full_name = this_user[1].toLowerCase() + ' ' + this_user[2].toLowerCase();
					lup = lookup_string.split(' ');
					if (lup.length > 1) {
						if (full_name.search(lup[0]) > -1 && full_name.search(lup[1]) > -1) {
							add_me = true;
						}
					} else {
						if (full_name.search(lookup_string) > -1) {
							add_me = true;
						}
					}				
					break;
				case 'address':
					term = this_user[3].toLowerCase();
					if (term.search(lookup_string) > -1) {
						add_me = true;
					}
					break;
				case 'email':
					term = this_user[4].toLowerCase();
					if (term.search(lookup_string) > -1) {
						add_me = true;
					}
					break;
			}
			
			// if add_me is true, check the overflow
			// as long as we're under the overflow, add the result
			// otherwise, increment overflow
			if (add_me == true) {
				if (add_cnt > 6) {
					overflow_cnt++;
				} else {
					this.add_row_to_lookup_results(this_user);
					add_cnt++;
				}
			} // end :: if checking for add_me
			
		} // end :: for looping users
	
		// if there is overflow, make a message to let the user know
		if (overflow_cnt > 0) {
			var overflow_notice = new Element('div', {
				'html' : '... and ' + overflow_cnt + ' more',
				'class' : 'bold red user_picker_control'
			}).inject(this.results_container);
		}
		
	},
	
	
	add_row_to_lookup_results : function (user_data) {
		//console.log(user_data)
		if (this.picker_open != true || this.preview_open == true)
			return;

		var one_result = new Element('div', {
			'styles' : {
				'border-bottom' : '1px dotted grey',
				'padding-top' : '3px',
				'padding-bottom' : '3px'
			},
			'class' : 'user_picker_control'
		});
		
		var user_img = new Element('img', {
			'src' : 'site/img/icons/user.png',
			'class' : 'user_picker_control'
		}).inject(one_result);
		var user_name = new Element('span', {
			'html' : (user_data[0] < 1000 ? '[STAFF] ' : '') + user_data[1] + ' ' + user_data[2],
			'class' : 'bold mediumText darkblue user_picker_control'
		}).inject(one_result);
		var select_img = new Element('img', {
			'src' : 'site/img/icons/check.png',
			'rel' : user_data[0],
			'styles' : {
				'float' : 'right'
			},
			'class' : 'user_picker_control'
		}).inject(one_result).addEvent('click', this.select.bind(this));;
		var preview_img = new Element('img', {
			'src' : 'site/img/icons/view.png',
			'rel' : user_data[0],
			'styles' : {
				'float' : 'right'
			},
			'class' : 'user_picker_control'
		}).inject(one_result).addEvent('click', this.preview.bind(this));
		one_result.inject(this.results_container);	
		var cb = new Element('div', {
			'class' : 'clearBoth'
		}).inject(one_result);
		var member_type = new Element('div', {
			'html' : (user_data[0] < 1000 ? 'Staff ' : '') + user_data[8],
			'class' : 'smallText user_picker_control',
			'styles' : {
				'float' : 'left'
			}			
		}).inject(one_result);
		var cb = new Element('div', {
			'class' : 'clearBoth'
		}).inject(one_result);
	},
	
	
	close : function (e, force) {
		if (this.preview_open == true || this.picker_open == true) {
			if ((typeof e == 'undefined' || typeof e.target == 'undefined') && typeof force == 'undefined') {
				return;
			}
			click_outside = false;
			if (typeof e != 'undefined' && typeof e.target != 'undefined' && !e.target.hasClass('user_picker_control')) {
				click_outside = true;
			}
			
			if (click_outside || force) {
				this.container.dispose();
				if (typeof this.preview_container != 'undefined')
					this.preview_container.dispose();
				//console.log('close has fired');
				this.picker_open = false;
				this.preview_open = false;
			}
		}
	},

	check_preview : function () {
		if (this.preview_open == true)
			this.close_preview();
	},
	close_preview : function () {
		if (this.preview_open == false)
			return;
		var tweenObj = new Fx.Tween(this.results_container, {
			'link' : 'chain',
			'duration' : 'short'		
		}).addEvent('chainComplete', this.remove_preview.bind(this));
		tweenObj.start('width', '340px');
		this.preview_open = false;
		this.picker_open = true;
	},
	remove_preview : function () {
		this.preview_container.dispose();
		this.preview_open = false;
		this.picker_open = true;		
	},
	preview : function (e) {
		if (this.picker_open == false || this.preview_open == true)
			return;

		this.preview_open = true;
		this.picker_open = false;
		
		var user_id = e.target.get('rel');
		
		var user_data = this.userdata[user_id];

		var br = new Element('br').addClass('user_picker_control');

		this.preview_container = new Element('div', {
			'class' : 'mediumText black user_picker_control',
			'styles' : {
				'margin-top' : '8px',
				'height' : '146px',
				'overflow' : 'hidden',
				'float' : 'left',
				'width' : '340px'
			}
		});
		
		
		var picture_div = new Element('div', {
			'styles' : {
				'float' : 'left'
			}, 
			'class' : 'user_picker_control'
		}).inject(this.preview_container);
		var pic = new Element('img', {
			'src' : URL+'site/img/users/' + user_data[9],
			'class' : 'user_picker_control',
			'styles' : {
				'height': '90px',
				'width': '90px'
			}
		}).inject(picture_div);
		
		var details_div = new Element('div', {
			'styles' : {
				'float' : 'left',
				'margin-left' : '6px'
			}, 
			'class' : 'user_picker_control'
		}).inject(this.preview_container);

		var name_span = new Element('span', {
			'class' : 'bold largeText user_picker_control',
			'html' : user_data[1] + ' ' + user_data[2]
		}).inject(details_div);
		br.clone().inject(details_div)
		
		var membership_span = new Element('span', {
			'html' : '<b>Member Type:</b>' + (user_data[0] < 1000 ? 'Staff ' : '') + user_data[8],
			'class' : 'user_picker_control'	
		}).inject(details_div);
		br.clone().inject(details_div)

		var address_span = new Element('span', {
			'html' : user_data[3] + '<br />' + user_data[5] + ', ' + user_data[6] + ' ' + user_data[7] + '<br />' + user_data[10],
			'class' : 'user_picker_control'
		}).inject(details_div);
		
		br.clone().addClass('clearBoth').inject(this.preview_container);		
		br.clone().inject(this.preview_container);


		var control_container = new Element('p').inject(this.preview_container);

		var select_link = new Element('a', {
			'class' : 'bottom_list_control user_picker_control',
			'rel' : user_data[0]
		}).inject(control_container).addEvent('click', this.select.bind(this));
		var select_icon = new Element('img', {
			'src' : URL+'site/img/icons/check.png', 
			'class' : 'user_picker_control',
			'rel' : user_data[0]
		}).inject(select_link).addEvent('click', function () {this.getParent().fireEvent('click')});
		var select_text = new Element('span', {
			'html' : ' select user', 
			'class' : 'user_picker_control',
			'rel' : user_data[0]
		}).inject(select_link).addEvent('click', function () {this.getParent().fireEvent('click')});

		var clear_link = new Element('a', {
			'class' : 'bottom_list_control user_picker_control'
		}).inject(control_container).addEvent('click', this.close_preview.bind(this));
		var clear_icon = new Element('img', {
			'src' : URL+'site/img/icons/clear.png', 
			'class' : 'user_picker_control'
		}).inject(clear_link).addEvent('click', this.close_preview.bind(this));
		var clear_text = new Element('span', {
			'html' : ' close preview', 
			'class' : 'user_picker_control'
		}).inject(clear_link).addEvent('click', this.close_preview.bind(this));
				

		this.preview_container.inject(this.container);
		
		
		// slide out the results
		this.results_container.tween('width', '0');		

		
	},
	
	select : function (e) {
		//console.log('SELECT - picker open:'+this.picker_open)
		if (!this.picker_open && !this.preview_open)
			return;

		//console.log('SELECT - e:')
		//console.log(e)
		if (typeof e == 'undefined')
			return ;
		var user_id = e.target.get('rel');
		//console.log('SELECT - e rel:' + user_id)
		this.display_target.set('html', this.userdata[user_id][1] + ' ' + this.userdata[user_id][2])
		this.data_target.set('value', this.userdata[user_id][0])
		this.close(false, true);
		
		if ($chk(this.options.onChange)) {
			if (typeof this.options.onChange != 'function')
				this.options.onChange = eval(this.options.onChange);
			this.options.onChange(this);
		}
		
	},
	
	update_display : function () {
		if (this.data_target.value > 0)
			this.display_target.set('html', this.userdata[this.data_target.value][1] + ' ' + this.userdata[this.data_target.value][2])
		else
			this.display_target.set('html', '(no user selected)'); 
	},
	
	clear : function () {
		this.display_target.set('html', '(no user selected)');
		this.data_target.set('value', '0');
	}
});


/* END site/form
*************************************************************/

/**************************************************************
 BEGIN site/paginating_table                                           */
//
// new PaginatingTable( 'my_table', 'ul_for_paginating', {
//   per_page: 10,     // How many rows per page?
//   current_page: 1,  // What page to start on when initialized
//   offset_el: false, // What dom element to stick the offset in
//   cutoff_el: false, // What dom element to stick the cutoff in
//   details: false    // Do we have hidden/collapsable rows?
// });
//
// The above were the defaults.  You could also pass an array of paginators
// instead of just one.
//
// Requires mootools Class, Array, Function, Element, Element.Selectors,
// Element.Event, and you should probably get Window.DomReady if you're smart.
//

var PaginatingTable = new Class({

  Implements: Options,
  
  options: {
    per_page: 10,
    current_page: 1,
    offset_el: false,
    cutoff_el: false,
    details: false
  },
  
  initialize: function( table, ids, options ) {
    this.table = $(table);
    this.setOptions(options);
    
    this.tbody = this.table.getElement('tbody');
    
    if (this.options.offset_el)
      this.options.offset_el = $(this.options.offset_el);
    if (this.options.cutoff_el)
      this.options.cutoff_el = $(this.options.cutoff_el);

    this.paginators = ($type(ids) == 'array') ? ids.map($) : [$(ids)];

    if (this.options.details) {
      this.options.per_page = this.options.per_page * 2
    }

    this.update_pages();
  },

  update_pages: function(){
    this.pages = Math.ceil( this.tbody.getChildren().length / this.options.per_page );
    this.create_pagination();
    this.to_page( 1 );
  },

  to_page: function( page_num ) {
    page_num = page_num.toInt();
    if (page_num > this.pages || page_num < 1) return;
    this.current_page = page_num;
    this.low_limit  = this.options.per_page * ( this.current_page - 1 );
    this.high_limit = this.options.per_page * this.current_page;
    var trs = this.tbody.getChildren();
    if (trs.length < this.high_limit) this.high_limit = trs.length;
    for (var i = 0, j = trs.length; i < j; i++) {
      trs[i].style.display = (this.low_limit  <= i && this.high_limit > i) ? '' : 'none';
    }
    this.paginators.each(function(paginator){
      var as = paginator.getElements('a').removeClass('currentPage');
      as[this.current_page].addClass('currentPage');
    }, this);
    if (this.options.offset_el)
      this.options.offset_el.set('text', Math.ceil( this.low_limit / ( this.options.details ? 2 : 1 ) + 1 ) );
    if (this.options.cutoff_el)
      this.options.cutoff_el.set('text', ( this.high_limit / ( this.options.details ? 2 : 1 ) ) );
  },

  to_next_page: function() {
    this.to_page( this.current_page + 1 );
  },

  to_prev_page: function() {
    this.to_page( this.current_page - 1 );
  },

  create_pagination: function() {
    this.paginators.each(function(paginator){
      paginator.empty();
      this.create_pagination_node( '&#171;', function(evt){
        var evt = new Event( evt );
        this.to_prev_page();
        evt.stop();
        return false;
      }).injectInside( paginator );
      for (var page=1; page <= this.pages; page++){
        this.create_pagination_node( page, function(evt){
          var evt = new Event( evt );
          this.to_page( evt.target.get( 'text' ) );
          evt.stop();
          return false;
        }).injectInside( paginator );  
      }
      this.create_pagination_node( '&#187;', function(evt){
        var evt = new Event( evt );
        this.to_next_page();
        evt.stop();
        return false;
      }).injectInside( paginator );
    }.bind( this ));
  },

  create_pagination_node: function( text, evt ) {
    var span = new Element( 'span' ).set( 'html', text );
    if (text == '&#171;'){
      var a = new Element( 'a', { 'href': '#', 'class': 'previous-page' }).addEvent( 'click', evt.bind( this ) );
    } else if (text == '&#187;'){
      var a = new Element( 'a', { 'href': '#', 'class': 'next-page' }).addEvent( 'click', evt.bind( this ) );
    } else {
      var a = new Element( 'a', { 'href': '#'}).addEvent( 'click', evt.bind( this ) );
    }
    var li   = new Element( 'li' );
    span.injectInside( a.injectInside( li ) );
    return li;
  }

});


/* END site/paginating_table
*************************************************************/

/**************************************************************
 BEGIN site/pulseFade                                           */
/* example

			var pf = new PulseFade(ele,{
				min: '#c30000',
				max: '#ffffff',
				duration: 200,
				times: 4,
				property: 'backgroundColor',
				onComplete: function() {
		//			alert('complete!');
				},
				onStart: function() {
		//			alert('started!');
				},
				onStop: function() {
		//			alert('stopped!');
				},
				onTick: function() {
		//			alert('tick!');
				}
			}).start();
*/

var PulseFade = new Class({
		
	//implements
	Implements: [Options,Events],

	//options
	options: {
		min: 0,
		max: 1,
		duration: 200,
		times: 5,
		property: 'opacity'
	},
	
	//initialization
	initialize: function(el,options) {
		//set options
		this.setOptions(options);
		this.element = $(el);
		this.times = 0;
	},
	
	//starts the pulse fade
	start: function(times) {
		if(!times) times = this.options.times * 2;
		this.running = 1;
		this.fireEvent('start').run(times -1);
	},
	
	//stops the pulse fade
	stop: function() {
		this.running = 0;
		this.fireEvent('stop');
	},
	
	//runs the shizzle
	run: function(times) {
		//make it happen
		var self = this;
		var to = self.element.getStyle(self.options.property) == self.options.min ? self.options.max : self.options.min;
		self.fx = new Fx.Tween(self.element,{
			duration: self.options.duration / 2,
			onComplete: function() {
				self.fireEvent('tick');
				if(self.running && times)
				{
					self.run(times-1);
				}
				else
				{
					self.fireEvent('complete');
				}
			}
		}).start(self.options.property,to);
	}
});





/* END site/pulseFade
*************************************************************/

/**************************************************************
 BEGIN site/sorting_table                                           */
//
// new SortingTable( 'my_table', {
//   zebra: true,                        // Stripe the table, also on initialize
//   details: false,                     // Has details every other row
//   paginator: false,                   // Pass a paginator object
//   onSorted: function(){},             // Callback to run after sort
//   dont_sort_class: 'nosort',          // Class name on th's that don't sort
//   forward_sort_class: 'forward_sort', // Class applied to forward sort th's
//   reverse_sort_class: 'reverse_sort'  // Class applied to reverse sort th's
// });
//
// The above were the defaults.  The regexes in load_conversions test a cell
// begin sorted for a match, then use that conversion for all elements on that
// column.
//
// Requires mootools Class, Array, Function, Element, Element.Selectors,
// Element.Event, and you should probably get Window.DomReady if you're smart.
//

var SortingTable = new Class({

  Implements: Options,
  
  options: {
    zebra: true,
    details: false,
    paginator: false,
    onSorted: $empty,
    dont_sort_class: 'nosort',
    forward_sort_class: 'forward_sort',
    reverse_sort_class: 'reverse_sort'
  },

  initialize: function( table, options ) {
    this.table = $(table);
    this.filter = $(table+'_filter_text');
    this.filter_col = $(table+'_filter_col');
    
    this.setOptions(options);
    
    this.tbody = this.table.getElement('tbody');
    if (this.options.zebra) {
      SortingTable.stripe_table(this.tbody.getChildren());
    }

	if (this.table.getElement('thead')) {
    this.headers = this.table.getElement('thead').getElements('th');
    this.headers.each(function( header, index ) {
      if (header.hasClass( this.options.dont_sort_class )) { return }
      header.store( 'column', index )
      header.setStyle('cursor', 'pointer')
      header.addEvent( 'mousedown', function(evt){
        this.sort_by_header( evt.target );
        if ( this.options.paginator) this.options.paginator.to_page( 1 );
      }.bind( this ) );
    }, this);}

	if (this.filter) {
	this.filter.addEvent( 'keyup', function(evt){
		this.search_in_rows(evt.target);
	}.bind( this ) );}

    this.load_conversions();
  },

  search_in_rows: function (ele) {
 		
 		// check for target 
 		if (this.filter_col.value.length < 1) {
		 	return;
		 }
 		
	    var rows = [];
	    
	    var before = this.tbody.getPrevious();
	    this.tbody.dispose();
	        
	    var trs = this.tbody.getChildren();
	    while ( row = trs.shift() ) {
	      row = { row: row.dispose() };
	      if ( this.options.details ) {
	        row.detail = trs.shift().dispose();
	      }
	      rows.unshift( row );
	    }

	    var index = 0;
	    var added_this_row = false;
	    while ( row = rows.shift() ) {
	    	added_this_row = false;
			var children = row.row.getChildren();
			for (i in children) {
				if (typeof children[i].get == 'function') {
					var this_col_name = children[i].get('rel');
					if (this.filter_col.value != this_col_name)
						continue;
					var this_col_data = children[i].get('html');

					this.tbody.appendChild(row.row);
					if (this_col_data.contains(this.filter.value)) {
						row.row.setStyle('display', '');
						added_this_row = true;
						row.row.set('rel', '');
						continue;
					} else {
						row.row.setStyle('display', 'none');
						row.row.set('rel', 'hide');
					}
				}
			}
	    	
	      //this.tbody.appendChild(row.row);
			if (added_this_row) {
		      if (row.detail) this.tbody.appendChild(row.detail);
		      if ( this.options.zebra ) {
		        row.row.className = row.row.className.replace( this.removeAltClassRe, '$1').clean();
		        if (row.detail)
		          row.detail.className = row.detail.className.replace( this.removeAltClassRe, '$1').clean();
		        if (index % 2) {
		          row.row.addClass( 'alt' ); 
		          if (row.detail) row.detail.addClass( 'alt' );
		        }
		      }
		      index++;
		    }
	    }
	   this.tbody.inject(before, 'after');
	   //this.fireEvent('sorted', this); 
  		this.options.paginator.update_pages();
  		return;
  },

  sort_by_header: function( header ){

    var rows = [];
    
    var before = this.tbody.getPrevious();
    this.tbody.dispose();
        
    var trs = this.tbody.getChildren();
    while ( row = trs.shift() ) {
      row = { row: row.dispose() };
      if ( this.options.details ) {
        row.detail = trs.shift().dispose();
      }
      rows.unshift( row );
    }
    
    if ( this.sort_column >= 0 &&
         this.sort_column == header.retrieve('column') ) {
      // They were pulled off in reverse
      if ( header.hasClass( this.options.reverse_sort_class ) ) {
        header.removeClass( this.options.reverse_sort_class );
        header.addClass( this.options.forward_sort_class );
      } else {
        header.removeClass( this.options.forward_sort_class );
        header.addClass( this.options.reverse_sort_class );
      }
    } else {
      this.headers.each(function(h){
        h.removeClass( this.options.forward_sort_class );
        h.removeClass( this.options.reverse_sort_class );
      }, this);
      this.sort_column = header.retrieve('column');
      if (header.retrieve('conversion_function')) {
        this.conversion_matcher = header.retrieve('conversion_matcher');
        this.conversion_function = header.retrieve('conversion_function');
      } else {
        this.conversion_function = false;
        rows.some(function(row){
          var to_match = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          if (to_match == '') return false;
          this.conversions.some(function(conversion){
            if (conversion.matcher.test( to_match )){
              this.conversion_matcher = conversion.matcher;
              this.conversion_function = conversion.conversion_function;
              return true;
            }
            return false;
          }, this);
          return !!(this.conversion_function);
        }, this);
        header.store('conversion_function', this.conversion_function );
        header.store('conversion_matcher', this.conversion_matcher );
      }
      header.addClass( this.options.forward_sort_class );
      rows.each(function(row){
        var compare_value = this.conversion_function( row );
        row.toString = function(){
          return compare_value;
        };
      }, this);
      rows.sort();
    }

    var index = 0;
    while ( row = rows.shift() ) {
      this.tbody.appendChild(row.row);
      if (row.detail) this.tbody.appendChild(row.detail);
      if ( this.options.zebra ) {
        row.row.className = row.row.className.replace( this.removeAltClassRe, '$1').clean();
        if (row.detail)
          row.detail.className = row.detail.className.replace( this.removeAltClassRe, '$1').clean();
        if (index % 2) {
          row.row.addClass( 'alt' ); 
          if (row.detail) row.detail.addClass( 'alt' );
        }
      }
      index++;
    }
   this.tbody.inject(before, 'after');
   //this.fireEvent('sorted', this);
  },

  load_conversions: function() {
    this.conversions = $A([
      // 1.75 MB, 301 GB, 34 KB, 8 TB
      { matcher: /([0-9.]{1,8}).*([KMGT]{1})B/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          cell = this.conversion_matcher.exec( cell );
          if (!cell) { return '0' }
          if (cell[2] == 'M') {
            sort_val = '1';
          } else if (cell[2] == 'G') {
            sort_val = '2';
          } else if (cell[2] == 'T') {
            sort_val = '3';
          } else {
            sort_val = '0';
          }
          var i = cell[1].indexOf('.')
          if (i == -1) {
            post = '00'
          } else {
            var dec = cell[1].split('.');
            cell[1] = dec[0];
            post = dec[1].concat('00'.substr(0,2-dec[1].length));
          }
          return sort_val.concat('00000000'.substr(0,2-cell[1].length).concat(cell[1])).concat(post);
        }
      },
      // 1 day ago, 4 days ago, 38 years ago, 1 month ago
      { matcher: /(\d{1,2}) (.{3,6}) ago/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          cell = this.conversion_matcher.exec( cell );
          if (!cell) { return '0' }
          var sort_val;
          if (cell[2].indexOf('month') != -1) {
            sort_val = '1';
          } else if (cell[2].indexOf('year') != -1) {
            sort_val = '2';
          } else {
            sort_val = '0';
          }
          return sort_val.concat('00'.substr(0,2-cell[1].length).concat(cell[1]));
        }
      },
      // Currency
      { matcher: /((\d{1}\.\d{2}|\d{2}\.\d{2}|\d{3}\.\d{2}|\d{4}\.\d{2}|\d{5}\.\d{2}|\d{6}\.\d{2}))/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          cell = cell.replace(/[^\d]/g, "");
          return '00000000000000000000000000000000'.substr(0,32-cell.length).concat(cell);
        }
      },
      // YYYY-MM-DD, YYYY-m-d
      { matcher: /(\d{4})-(\d{1,2})-(\d{1,2})/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          cell = this.conversion_matcher.exec( cell );
          return cell[1]+
                 '00'.substr(0,2-cell[2].length).concat(cell[2])+
                 '00'.substr(0,2-cell[3].length).concat(cell[3]);
        }
      },
      // Numbers
      { matcher: /^\d+$/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          return '00000000000000000000000000000000'.substr(0,32-cell.length).concat(cell);
        }
      },
      // Fallback 
      { matcher: /.*/,
        conversion_function: function( row ) {
          return $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
        }
      }
    ]);
  }

});

SortingTable.removeAltClassRe = new RegExp('(^|\\s)alt(?:\\s|$)');
SortingTable.implement({ removeAltClassRe: SortingTable.removeAltClassRe });

SortingTable.stripe_table = function ( tr_elements  ) {
  var counter = 0;
  tr_elements.each( function( tr ) {
    if ( !tr.hasClass('collapsed') ) counter++;
    tr.className = tr.className.replace( this.removeAltClassRe, '$1').clean();
    if (counter % 2) tr.addClass( 'alt' );
  });
}


/* END site/sorting_table
*************************************************************/

/**************************************************************
 BEGIN site/spinner                                           */
/**
 * @class Spinner Control Widget
 *
 * @author Ken Snyder (kendsnyder at gmail dot com)
 * @date 2007-07-15
 * @version 1.0
 * @license Creative Commons Attribution 3.0 (http://creativecommons.org/licenses/by/3.0/)
 * @requires Prototype version 1.5.0 or newer
 * @tested in FF2, IE7, Safari 3, Opera 9
 */
SpinnerControl = new Class({
  /**
   * @constructor  Create the spinner using an input, an up button, and a down button
   *
   * @param string/Element  inputElement
   * @param string/Element  upElement
   * @param string/Element  downElement
   * @param object          options
   * Available Options:
   *   interval     The amount to increment (default=1)
   *   round        The number of decimal points to which to round (default=0)
   *   min          The lowest allowed value, false for no min (default=false)
   *   max          The highest allowed value, false for no max (default=false)
   *   prefix       String to prepend when updating (default='')
   *   suffix       String to append when updating (default='')
   *   data         An array giving a list of items through which to iterate (default=false)
   *   onIncrement  Function to call after incrementing
   *   onDecrement  Function to call after decrementing
   *   afterUpdate  Function to call after update of the value
   *   onStop       Function to call on click or mouseup
   * @return void
   */
  Implements: Options,
  options : {
	  interval: 1,
	  is_time: false,
	  round: 0,
	  min: false,
	  max: false,
	  prefix: '',
	  suffix: '',
	  data: false,
	  onIncrement: function () {},
	  onDecrement: function () {},
	  afterUpdate: function () {},
	  onStop: function () {}
  },

  initialize: function(inputElement, upElement, downElement, options) {
    // store the elements
    this.inputElement = $(inputElement);
    this.upElement = $(upElement);
    this.downElement = $(downElement);
    // store the options
	 this.setOptions(options);

    // set initial values
    this.reset();
    // build our update function
    this.buildUpdateFunction();
    // define the rate of increasing speed
    if (Browser.Engine.trident) {
      this.speedHash = {5: 300, 10: 175, 20: 90, 30: 17};
    } else {
      this.speedHash = {5: 250, 10: 85, 20: 35, 30: 10};
    }
    // attach listeners
    this.observe();
  },
  /**
   * Helper function to define the update function
   *
   * @return void
   */
  buildUpdateFunction: function() {
    // do we have a data list?
    if (this.options.data == false) {
      // no, we are an integer or decimal
      this.updateValue = function(multiplier) {
        // parse the value ignoring the substring
        var value = parseFloat(this.inputElement.value.replace(/^(.*?)([\-\d\.]+)(.*)$/, '$2'));
		if (this.options.is_time) {
			var ivalue = this.inputElement.value.replace(',', '');
			var tvalue = ivalue.split(' hours');
			if (tvalue.length > 1)
				hours = tvalue[0];
			else
				hours = 0;

			ivalue = ivalue.replace(hours + ' hours', '');
			var tvalue = ivalue.split(' minutes');
			if (tvalue.length > 1)
				minutes = tvalue[0];
			else
				minutes = 0;
			value = parseFloat(hours) + parseFloat(parseFloat(minutes) / 60);
		}

        if (!IsNumeric(value)) value = this.options.min || 0;
        // what are we adding
        if (multiplier == 1) {
          value = parseFloat(value) + parseFloat(this.options.interval)
        } else if (multiplier == -1) {
          value = parseFloat(value) - parseFloat(this.options.interval)
        }
        // ensure value falls between the min and max
        if (this.options.min !== false)
          value = Math.max(this.options.min, value);
        if (this.options.max !== false)
          value = Math.min(this.options.max, value);            
        this.setValue(value);
        // call our afterUpdate function
        this.options.afterUpdate(this);
      }.bind(this);
      // set an initial value if not given
      if (this.inputElement.value === '') {
        this.inputElement.value = this.options.min || 0;
      }
    } else if (this.options.data.constructor == Array && this.options.data.length) {
      // we have a data list
      // set the position pointer to the current or first element
      var current = this.options.data.indexOf(this.inputElement.value);
      this.pos = current == -1 ? 0 : current;
      // define our function
      this.updateValue = function(multiplier) {
        // advance the pointer forward or backward, wrapping between the last and first item
        this.pos = this.pos + multiplier;
        this.pos = this.pos < 0 ? this.options.data.length -1 : (
          this.pos > this.options.data.length - 1 ? 0 : this.pos
        );
        // update the value to the prefix, plus the rounded number, plus the suffix
        this.setValue(this.options.data[this.pos]);
        // call our afterUpdate function
        this.options.afterUpdate(this);
      }.bind(this);
      // set an initial value if not given
      if (this.inputElement.value === '') {
        this.inputElement.value = this.options.data[0];
      }
    } else {
      // we have an invalid data option
      throw new Error('SpinnerControl.initialize(): invlalid value for options.data');
    } 
   
    
  },
  setValue: function(value, raw_value) {
	if ($chk(raw_value) && raw_value == true) {
		this.inputElement.value = value;
		return;
	}
  	if (this.options.is_time) {
		hours = Math.floor(value);
		minutes = value - hours;
		minutes = Math.floor(60 * minutes);
		
		value = '';
		if (hours > 0)
			value = hours + ' hours';
		
		if (minutes > 0) {
			if (hours > 0)
				value += ', '
			value += minutes + ' minutes';
		}
  		this.inputElement.value = value;
	} else {  
    	this.inputElement.value = this.options.prefix + value + this.options.suffix;
	}  
  },

  /**
   * Helper function to attach listeners
   */
  observe: function() {
    // define a pre-bound stop function
    var stop = this.stop.bind(this);
    // observe the input
    this.inputElement
      // begin incrementing at start of a keypress
      .addEvent('keydown', this.keyStart.bind(this))
      // stop incrementing at the end of a keypress
      .addEvent('keyup', stop)
      // reformat and enforce min-max for typed values
      .addEvent('blur', this.updateValue.bind(this));
    // observe the up element
    this.upElement
      // begin incrementing at start of click
      .addEvent('mousedown', this.clickStart.bind(this))
      // stop incrementing at end of click
      .addEvent('mouseup', stop)
      // in the case of a click and drag, also stop
      .addEvent('mouseout', stop);
    // observe the down element
    this.downElement
      // begin decrementing at start of click
      .addEvent('mousedown', this.clickStart.bind(this))
      // stop decrementing at end of click
      .addEvent('mouseup', stop)
      // in the case of a click and drag, also stop
      .addEvent('mouseout', stop);
  },
  /**
   * Start incrementing or decrementing based on a pressed key
   *
   * @event keydown on this.inputElement
   * @param object evt
   * @return void
   */
  keyStart: function(evt) {
    if (this.running == false) {
      if (evt.keyCode == Event.KEY_UP) {
        this.running = 'key';
        this.increment();
      } else if (evt.keyCode == Event.KEY_DOWN) {
        this.running = 'key';
        this.decrement();
      }
    }
  },
  /**
   * Start incrementing or decrementing based on a mousedown action
   *
   * @param boolean multiplier  If multipler is 1, increment
   * @return void
   */  
  clickStart: function(e) {
  	//console.log(this)
    this.running = 'mouse';
	if (e.target.hasClass('spin_up'))
		var multiplier = 1;
	else
		var multiplier = -1;		

	var ivalue = this.inputElement.value;
	
	ivalue = ivalue.replace(this.options.prefix, '');
	ivalue = ivalue.replace(this.options.suffix, '');

	if (this.options.is_time) {
		var ivalue = this.inputElement.value.replace(',', '');
		var tvalue = ivalue.split(' hours');
		if (tvalue.length > 1)
			hours = tvalue[0];
		else
			hours = 0;

		ivalue = ivalue.replace(hours + ' hours', '');
		var tvalue = ivalue.split(' minutes');
		if (tvalue.length > 1)
			minutes = tvalue[0];
		else
			minutes = 0;
		ivalue = parseFloat(hours) + parseFloat(parseFloat(minutes) / 60);
	}

    if (multiplier == 1 && (parseFloat(ivalue) + parseFloat(this.options.interval)) <= this.options.max) {
      this.increment();
    } else if (parseFloat(ivalue) - parseFloat(this.options.interval) >= this.options.min) {
      this.decrement();
    }
  },
  /**
   * Set to resting state
   *
   * return @void
   */
  reset: function() {
    // blur the up/down buttons if we got started by clicking
    if (this.running == 'mouse') {
      this.upElement.blur();
      this.downElement.blur();      
    }
    this.running = false;
    this.iterations = 0;
  },
  /**
   * Reset and clear timeout
   *
   * @return void
   */
  stop: function() {
    this.reset();
    window.clearTimeout(this.timeout);
    this.options.onStop(this);
  },
  /**
   * Increment the value
   *
   * @return void
   */
  increment: function() {
    this.updateValue(1);
    this.timeout = window.setTimeout(this.increment.bind(this), this.getSpeed());
    this.options.onIncrement(this);
  },
  /**
   * Decrement the value
   *
   * @return void
   */  
  decrement: function() {
    this.updateValue(-1);
    this.timeout = window.setTimeout(this.decrement.bind(this), this.getSpeed());
    this.options.onDecrement(this);
  },
  /**
   * Get the delay for the next timeout
   * Overwrite this function for custom speed schemes
   *
   * @return integer
   */  
  getSpeed: function() {
    this.iterations++;
    for (var iterations in this.speedHash) {
      if (this.iterations < iterations) {
        return this.speedHash[iterations];
      }
    }
    return this.speedHash[30];
  } 
});


/* END site/spinner
*************************************************************/

/**************************************************************
 BEGIN site/timePicker                                           */
var dateTimePicker = new Class({	
	Implements: Options,

	// working date, which we will keep modifying to render the calendars
	d: '',
	
	// just so that we need not request it over and over
	today: '',
	
	// current user-choice in date object format
	choice: {}, 
	
	// size of body, used to animate the sliding
	bodysize: {}, 
	
	// to check availability of next/previous buttons
	limit: {}, 
	
	// element references:
	attachTo: null,    // selector for target inputs
	picker: null,      // main datepicker container
	slider: null,      // slider that contains both oldContents and newContents, used to animate between 2 different views
	oldContents: null, // used in animating from-view to new-view
	newContents: null, // used in animating from-view to new-view
	input: null,       // original input element (used for input/output)
	visual: null,      // visible input (used for rendering)
	
	options: { 
		pickerClass: 'dp_ui',
		days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
		months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
		dayShort: 2,
		monthShort: 3,
		startDay: 1, // Sunday (0) through Saturday (6) - be aware that this may affect your layout, since the days on the right might have a different margin
		timePicker: false,
		yearPicker: true,
		yearsPerPage: 20,
		format: 'm/d/Y',
		allowEmpty: true,
		inputOutputFormat: 'U', // default to unix timestamp
		animationDuration: 400,
		useFadeInOut: !Browser.Engine.trident, // dont animate fade-in/fade-out for IE
		startView: 'month', // allowed values: {time, month, year, decades}
		positionOffset: { x: 0, y: 0 },
		minDate: null, // { date: '[date-string]', format: '[date-string-interpretation-format]' }
		maxDate: null, // same as minDate
		debug: false,
		toggleElement: null,

		mode: 'dateTime',
		timeBlockIncrement: 15,
		timeBlocks: false,
		
		// and some event hooks:
		onShow: $empty,   // triggered when the datepicker pops up
		onClose: $empty,  // triggered after the datepicker is closed (destroyed)
		onSelect: $empty  // triggered when a date is selected
	},

	initialize: function(element, options) {
		this.setOptions(options)

		this.valueElement = element;
		this.input = element;

		var next_ele = this.valueElement.getNext();
		if (next_ele.hasClass('pickerToggle'))
			this.options.toggleElement = this.valueElement.getNext();

		if (this.options.mode == 'time') {
			this.options.timePicker = true;
			this.options.yearPicker = false;
			this.options.datePicker = false;
			this.options.format = 'h:i A';
			this.options.startView = 'time';
		} else if (this.options.mode == 'date_time') {
			this.options.format = 'm/d/Y @ h:i A';
			this.options.timePicker = true;
		}
		this.formatMinMaxDates();
		this.mode = 'destroyed';

		setTimeout(this.apply_events.bind(this), 500);

		document.addEvent('mousedown', this.close.bind(this));

		this.attach();			
		
	},
	
	apply_events: function () {
		if (typeof this.options.onShow == 'string')
			this.options.onShow = eval(this.options.onShow)
		if (typeof this.options.onClose == 'string')
			this.options.onClose = eval(this.options.onClose)
		if (typeof this.options.onSelect == 'string')
			this.options.onSelect = eval(this.options.onSelect)
	},
	
	
	formatMinMaxDates: function() {
		if (this.options.minDate && this.options.minDate.format) {
			this.options.minDate = this.unformat(this.options.minDate.date, this.options.minDate.format);
		}
		if (this.options.maxDate && this.options.maxDate.format) {
			this.options.maxDate = this.unformat(this.options.maxDate.date, this.options.maxDate.format);
			this.options.maxDate.setHours(23);
			this.options.maxDate.setMinutes(59);
			this.options.maxDate.setSeconds(59);
		}
	},
	
	attach: function() {

		// never double attach
		if (this.valueElement.retrieve('jso_ref')) return;

		// determine starting value(s)
		if ($chk(this.valueElement.get('value')) && this.valueElement.get('value') > 0) {
			var init_clone_val = this.format(new Date(this.unformat(this.valueElement.get('value'), this.options.inputOutputFormat)), this.options.format);
		} else if (!this.options.allowEmpty) {
			var init_clone_val = this.format(new Date(), this.options.format);
		} else {
			var init_clone_val = '';
		}

		// create clone
		var display = this.valueElement.getStyle('display');
		this.clone = this.valueElement
			.setStyle('display', this.options.debug ? display : 'none')
			.store('jso_ref', this) // to prevent double attachment...
			.clone()
			.store('jso_ref', this) // ...even for the clone (!)
			.removeProperty('name')    // secure clean (form)submission
			.setStyle('display', display)
			.set('value', init_clone_val)
			.set('readonly', '1')
			.inject(this.valueElement, 'after');

		this.visual = this.clone;

		// events
		if ($chk(this.options.toggleElement)) {
			this.options.toggleElement.setStyle('cursor', 'pointer')
				.addEvent('click', this.onClick.bind(this));
		}
		this.clone.addEvent('blur', this.onBlur.bind(this))
			.addEvent('click', this.onClick.bind(this));

	},
	
	onBlur: function (e) {
		//console.log('dtp blur');
		//console.log(this.clone)
		//this.valueElement.set('value', this.clone.get('value'));
	},	

	onClick: function (e) {
		this.onFocus(this.valueElement, this.clone);
	},
	onFocus: function(original_input, visual_input) {
		var init_visual_date, d = visual_input.getCoordinates();
		
		if ($chk(original_input.get('value')) && original_input.get('value') > 0) {
			init_visual_date = this.unformat(original_input.get('value'), this.options.inputOutputFormat).valueOf();
		} else {
			init_visual_date = new Date();
			if ($chk(this.options.maxDate) && init_visual_date.valueOf() > this.options.maxDate.valueOf()) {
				init_visual_date = new Date(this.options.maxDate.valueOf());
			}
			if ($chk(this.options.minDate) && init_visual_date.valueOf() < this.options.minDate.valueOf()) {
				init_visual_date = new Date(this.options.minDate.valueOf());
			}
		}
		this.show({ left: d.left + this.options.positionOffset.x, top: d.top + d.height + this.options.positionOffset.y }, init_visual_date);
		this.input = original_input;
		this.visual = visual_input;

		if (typeof this.options.onShow == 'function')
			this.options.onShow();
		else if (typeof this.options.onShow == 'string')
			eval(this.options.onShow)
	},
	
	dateToObject: function(d) {
		return {
			year: d.getFullYear(),
			month: d.getMonth(),
			day: d.getDate(),
			hours: d.getHours(),
			minutes: d.getMinutes(),
			seconds: d.getSeconds()
		};
	},
	
	dateFromObject: function(values) {
		var d = new Date();
		d.setDate(1);
		['year', 'month', 'day', 'hours', 'minutes', 'seconds'].each(function(type) {
			var v = values[type];
			if (!$chk(v)) return;
			switch (type) {
				case 'day': d.setDate(v); break;
				case 'month': d.setMonth(v); break;
				case 'year': d.setFullYear(v); break;
				case 'hours': d.setHours(v); break;
				case 'minutes': d.setMinutes(v); break;
				case 'seconds': d.setSeconds(v); break;
			}
		});
		return d;
	},
	
	show: function(position, timestamp) {
		if (this.mode != 'destroyed')
			this.destroy();
		
		this.formatMinMaxDates();
		if ($chk(timestamp)) {
			this.d = new Date(timestamp);
		} else {
			this.d = new Date();
		}
		this.today = new Date();
		this.choice = this.dateToObject(this.d);
		this.mode = (this.options.startView == 'time' && !this.options.timePicker) ? 'month' : this.options.startView;
		this.render();
		this.picker.setStyles(position);
		
		if (this.options.mode == 'time') {
			this.picker.getElement('.hour').focus();
		}
	},
	
	render: function(fx) {
		if (!$chk(this.picker)) {
			this.constructPicker();
		} else {
			// swap contents so we can fill the newContents again and animate
			var o = this.oldContents;
			this.oldContents = this.newContents;
			this.newContents = o;
			this.newContents.empty();
		}
		
		// remember current working date
		var startDate = new Date(this.d.getTime());
		
		// intially assume both left and right are allowed
		this.limit = { right: false, left: false };
		
		// render! booty!
		if (this.mode == 'decades') {
			this.renderDecades();
		} else if (this.mode == 'year') {
			this.renderYear();
		} else if (this.mode == 'time') {
			this.renderTime();
			this.limit = { right: true, left: true }; // no left/right in timeview
		} else {
			this.renderMonth();
		}
		
		this.picker.getElement('.previous').setStyle('visibility', this.limit.left ? 'hidden' : 'visible');
		this.picker.getElement('.next').setStyle('visibility', this.limit.right ? 'hidden' : 'visible');
		this.picker.getElement('.titleText').setStyle('cursor', this.allowZoomOut() ? 'pointer' : 'default');
		
		// restore working date
		this.d = startDate;
		
		// if ever the opacity is set to '0' it was only to have us fade it in here
		// refer to the constructPicker() function, which instantiates the picker at opacity 0 when fading is desired
		if (this.picker.getStyle('opacity') == 0) {
			this.picker.tween('opacity', 0, 1);
		}
		
		// animate
		if ($chk(fx)) this.fx(fx);
	},
	
	fx: function(fx) {
		if (fx == 'right') {
			this.oldContents.setStyles({ left: 0, opacity: 1 });
			this.newContents.setStyles({ left: this.bodysize.x, opacity: 1 });
			this.slider.setStyle('left', 0).tween('left', 0, -this.bodysize.x);
		} else if (fx == 'left') {
			this.oldContents.setStyles({ left: this.bodysize.x, opacity: 1 });
			this.newContents.setStyles({ left: 0, opacity: 1 });
			this.slider.setStyle('left', -this.bodysize.x).tween('left', -this.bodysize.x, 0);
		} else if (fx == 'fade') {
			this.slider.setStyle('left', 0);
			this.oldContents.setStyle('left', 0).set('tween', { duration: this.options.animationDuration / 2 }).tween('opacity', 1, 0);
			this.newContents.setStyles({ opacity: 0, left: 0}).set('tween', { duration: this.options.animationDuration }).tween('opacity', 0, 1);
		}
	},
	
	constructPicker: function() {
		this.picker = new Element('div', { 'class': this.options.pickerClass }).inject(document.body);
		if (this.options.useFadeInOut) {
			this.picker.setStyle('opacity', 0).set('tween', { duration: this.options.animationDuration });
		}
		
		var h = new Element('div', { 'class': 'header' }).inject(this.picker);
		var titlecontainer = new Element('div', { 'class': 'title' }).inject(h);
		new Element('div', { 'class': 'previous' }).addEvent('click', this.previous.bind(this)).set('text', '«').inject(h);
		new Element('div', { 'class': 'next' }).addEvent('click', this.next.bind(this)).set('text', '»').inject(h);
		new Element('div', { 'class': 'closeButton' }).addEvent('click', this.close.bindWithEvent(this, true)).set('text', 'x').inject(h);
		new Element('span', { 'class': 'titleText' }).addEvent('click', this.zoomOut.bind(this)).inject(titlecontainer);
		
		var b = new Element('div', { 'class': 'body' }).inject(this.picker);
		this.bodysize = b.getSize();
		this.slider = new Element('div', { styles: { position: 'absolute', top: 0, left: 0, width: 2 * this.bodysize.x, height: this.bodysize.y }})
					.set('tween', { duration: this.options.animationDuration, transition: Fx.Transitions.Quad.easeInOut }).inject(b);
		this.oldContents = new Element('div', { styles: { position: 'absolute', top: 0, left: this.bodysize.x, width: this.bodysize.x, height: this.bodysize.y }}).inject(this.slider);
		this.newContents = new Element('div', { styles: { position: 'absolute', top: 0, left: 0, width: this.bodysize.x, height: this.bodysize.y }}).inject(this.slider);
	},
	
	renderTime: function() {
		var container = new Element('div', { 'class': 'time' }).inject(this.newContents);
		
		if (this.options.mode == 'time') {
			this.picker.getElement('.titleText').set('text', 'Select a time');
		} else {
			this.picker.getElement('.titleText').set('text', this.format(this.d, 'j M, Y'));
		}

		in_pm = false;
		var curr_hours = this.d.getHours();

		if (curr_hours >= 12)
			in_pm = true;
		if (curr_hours > 12) {
			curr_hours -= 12;
		}
		
		if (curr_hours == 0)
			curr_hours = 12;

		var guid = random_string(12);

		var he = new Element('input', { type: 'text', 'class': 'hour', 'id': guid, 'name': guid})
			.set('value', this.leadZero(curr_hours))
			.addEvents({
				mousewheel: function(e) {
					var i = e.target, v = i.get('value').toInt();
					i.focus();
					if (e.wheel > 0) {
						v = (v < 12) ? v + 1 : 1;
					} else {
						v = (v > 1) ? v - 1 : 12;
					}
					i.set('value', this.leadZero(v));
					e.stop();
				}.bind(this)
			})
			.set('maxlength', 2)
			.inject(container);
			
		var tb = new Element('input', { type: 'text', 'class': 'minutes' })
			.set('value', this.leadZero(this.d.getMinutes()))
			.addEvents({
				mousewheel: function(e) {
					var i = e.target, v = i.get('value').toInt();
					i.focus();
					if (this.options.timeBlocks) {
						if (e.wheel > 0) {
							tv = v + parseFloat(this.options.timeBlockIncrement);
							if (tv >= 60)
								v = 0;
							else
								v = v + parseFloat(this.options.timeBlockIncrement);
						} else {
							tv = v - parseFloat(this.options.timeBlockIncrement);
							if (tv < 0)
								v = 60 - parseFloat(this.options.timeBlockIncrement);
							else
								v = v - parseFloat(this.options.timeBlockIncrement);
						}
					} else {			
						if (e.wheel > 0) {
							v = (v < 59) ? v + 1 : 0;
						} else {
							v = (v > 0) ? v - 1 : 59;
						}
					}
					i.set('value', this.leadZero(v));
					e.stop();
				}.bind(this)
			})
			.set('maxlength', 2)
			.inject(container);
		
		tb.set('value', this.leadZero(0));


		new Element('input', { id:'am_ele', type: 'button', 'class': 'timeblock am'})
			.addEvents({
				click: function(e) {
					var i = e.target;
					var tclass = i.className;

					if (tclass.indexOf('selected') != -1)
						return false;

					$('pm_ele').removeClass('selected');
					i.addClass('selected');

					e.stop();
				}.bind(this)
			}).inject(container).set('value', 'AM');
			
		new Element('input', { id:'pm_ele', type: 'button', 'class': 'timeblock pm'})
			.addEvents({
				click: function(e) {
					var i = e.target;
					var tclass = e.target.className;

					if (tclass.indexOf('selected') != -1)
						return false;

					$('am_ele').removeClass('selected');
					i.addClass('selected');

					e.stop();
				}.bind(this)
			}).inject(container).set('value', 'PM');



		if (in_pm == false)
			$('am_ele').addClass('selected');
		else
			$('pm_ele').addClass('selected');




		new Element('div', { 'class': 'separator' }).set('text', ':').inject(container);
		
		new Element('div', { 'class': 'separator' }).set('text', ':').inject(container);
		
		new Element('input', { type: 'submit', value: 'OK', 'class': 'ok' })
			.addEvents({
				click: function(e) {
					e.stop();
					var ampm = (($('pm_ele').className.indexOf('selected') != -1) ? 'pm' : 'am');
					am_pm_boost = 0;
					if (ampm == 'pm' && this.picker.getElement('.hour').get('value').toInt() != 12) {
						am_pm_boost = 12;
					} else if (ampm == 'am' && this.picker.getElement('.hour').get('value').toInt() == 12) {
						am_pm_boost = -12;
					}
					this.select($merge(this.dateToObject(this.d), { hours: this.picker.getElement('.hour').get('value').toInt() + am_pm_boost, minutes: this.picker.getElement('.minutes').get('value').toInt() }));
				}.bind(this)
			})
			.set('maxlength', 2)
			.inject(container);
		setTimeout('$("'+guid+'").focus();', 200);
	},
	
	renderMonth: function() {
		var month = this.d.getMonth();
		
		this.picker.getElement('.titleText').set('text', this.options.months[month] + ' ' + this.d.getFullYear());
		
		this.d.setDate(1);
		while (this.d.getDay() != this.options.startDay) {
			this.d.setDate(this.d.getDate() - 1);
		}
		
		var container = new Element('div', { 'class': 'days' }).inject(this.newContents);
		var titles = new Element('div', { 'class': 'titles' }).inject(container);
		var d, i, classes, e, weekcontainer;

		for (d = this.options.startDay; d < (this.options.startDay + 7); d++) {
			new Element('div', { 'class': 'title day day' + (d % 7) }).set('text', this.options.days[(d % 7)].substring(0,this.options.dayShort)).inject(titles);
		}
		
		var available = false;
		var t = this.today.toDateString();
		var currentChoice = this.dateFromObject(this.choice).toDateString();
		
		for (i = 0; i < 42; i++) {
			classes = [];
			classes.push('day');
			classes.push('day'+this.d.getDay());
			if (this.d.toDateString() == t) classes.push('today');
			if (this.d.toDateString() == currentChoice) classes.push('selected');
			if (this.d.getMonth() != month) classes.push('otherMonth');
			
			if (i % 7 == 0) {
				weekcontainer = new Element('div', { 'class': 'week week'+(Math.floor(i/7)) }).inject(container);
			}
			
			e = new Element('div', { 'class': classes.join(' ') }).set('text', this.d.getDate()).inject(weekcontainer);
			if (this.limited('date')) {
				e.addClass('unavailable');
				if (available) {
					this.limit.right = true;
				} else if (this.d.getMonth() == month) {
					this.limit.left = true;
				}
			} else {
				available = true;
				e.addEvent('click', function(e, d) {
					if (this.options.timePicker) {
						this.d.setDate(d.day);
						this.d.setMonth(d.month);
						this.mode = 'time';
						this.render('fade');
					} else {
						this.select(d);
					}
				}.bindWithEvent(this, { day: this.d.getDate(), month: this.d.getMonth(), year: this.d.getFullYear() }));
			}
			this.d.setDate(this.d.getDate() + 1);
		}
		if (!available) this.limit.right = true;
		
		if (this.options.monthPicker) {
			var g = {'day': '1', 'month': this.d.getMonth() , 'year' : this.d.getFullYear()};
			this.select(g);
			return;
		}
		
		
	},
	
	renderYear: function() {
		var month = this.today.getMonth();
		var thisyear = this.d.getFullYear() == this.today.getFullYear();
		var selectedyear = this.d.getFullYear() == this.choice.year;
		
		this.picker.getElement('.titleText').set('text', this.d.getFullYear());
		this.d.setMonth(0);
		
		var i, e;
		var available = false;
		var container = new Element('div', { 'class': 'months' }).inject(this.newContents);
		
		for (i = 0; i <= 11; i++) {
			e = new Element('div', { 'class': 'month month'+(i+1)+(i == month && thisyear ? ' today' : '')+(i == this.choice.month && selectedyear ? ' selected' : '') })
			.set('text', this.options.monthShort ? this.options.months[i].substring(0, this.options.monthShort) : this.options.months[i]).inject(container);
			
			if (this.limited('month')) {
				e.addClass('unavailable');
				if (available) {
					this.limit.right = true;
				} else {
					this.limit.left = true;
				}
			} else {
				available = true;
				e.addEvent('click', function(e, d) {
					this.d.setDate(1);
					this.d.setMonth(d);
					this.mode = 'month';
					this.render('fade');
				}.bindWithEvent(this, i));
			}
			this.d.setMonth(i);
		}
		if (!available) this.limit.right = true;
	},
	
	renderDecades: function() {
		// start neatly at interval (eg. 1980 instead of 1987)
		while (this.d.getFullYear() % this.options.yearsPerPage > 0) {
			this.d.setFullYear(this.d.getFullYear() - 1);
		}

		this.picker.getElement('.titleText').set('text', this.d.getFullYear() + '-' + (this.d.getFullYear() + this.options.yearsPerPage - 1));
		
		var i, y, e;
		var available = false;
		var container = new Element('div', { 'class': 'years' }).inject(this.newContents);
		
		if ($chk(this.options.minDate) && this.d.getFullYear() <= this.options.minDate.getFullYear()) {
			this.limit.left = true;
		}
		
		for (i = 0; i < this.options.yearsPerPage; i++) {
			y = this.d.getFullYear();
			e = new Element('div', { 'class': 'year year' + i + (y == this.today.getFullYear() ? ' today' : '') + (y == this.choice.year ? ' selected' : '') }).set('text', y).inject(container);
			
			if (this.limited('year')) {
				e.addClass('unavailable');
				if (available) {
					this.limit.right = true;
				} else {
					this.limit.left = true;
				}
			} else {
				available = true;
				e.addEvent('click', function(e, d) {
					this.d.setFullYear(d);
					this.mode = 'year';
					this.render('fade');
				}.bindWithEvent(this, y));
			}
			this.d.setFullYear(this.d.getFullYear() + 1);
		}
		if (!available) {
			this.limit.right = true;
		}
		if ($chk(this.options.maxDate) && this.d.getFullYear() >= this.options.maxDate.getFullYear()) {
			this.limit.right = true;
		}
	},
	
	limited: function(type) {
		var cs = $chk(this.options.minDate);
		var ce = $chk(this.options.maxDate);
		if (!cs && !ce) return false;
		
		switch (type) {
			case 'year':
				return (cs && this.d.getFullYear() < this.options.minDate.getFullYear()) || (ce && this.d.getFullYear() > this.options.maxDate.getFullYear());
				
			case 'month':
				// todo: there has got to be an easier way...?
				var ms = ('' + this.d.getFullYear() + this.leadZero(this.d.getMonth())).toInt();
				return cs && ms < ('' + this.options.minDate.getFullYear() + this.leadZero(this.options.minDate.getMonth())).toInt()
					|| ce && ms > ('' + this.options.maxDate.getFullYear() + this.leadZero(this.options.maxDate.getMonth())).toInt()
				
			case 'date':
				return (cs && this.d < this.options.minDate) || (ce && this.d > this.options.maxDate);
		}
	},
	
	allowZoomOut: function() {
		if (this.mode == 'time' && this.options.mode == 'time') return false;
		if (this.mode == 'decades') return false;
		if (this.mode == 'year' && !this.options.yearPicker) return false;
		return true;
	},
	
	zoomOut: function() {
		if (!this.allowZoomOut()) return;
		if (this.mode == 'year') {
			this.mode = 'decades';
		} else if (this.mode == 'time') {
			this.mode = 'month';
		} else {
			this.mode = 'year';
		}
		this.render('fade');
	},
	
	previous: function() {
		if (this.mode == 'decades') {
			this.d.setFullYear(this.d.getFullYear() - this.options.yearsPerPage);
		} else if (this.mode == 'year') {
			this.d.setFullYear(this.d.getFullYear() - 1);
		} else if (this.mode == 'month') {
			this.d.setMonth(this.d.getMonth() - 1);
		}
		this.render('left');
	},
	
	next: function() {
		if (this.mode == 'decades') {
			this.d.setFullYear(this.d.getFullYear() + this.options.yearsPerPage);
		} else if (this.mode == 'year') {
			this.d.setFullYear(this.d.getFullYear() + 1);
		} else if (this.mode == 'month') {
			this.d.setMonth(this.d.getMonth() + 1);
		}
		this.render('right');
	},
	
	close: function(e, force) {
		if (!$(this.picker)) return;
		var clickOutside = ($chk(e) && e.target != this.picker && !this.picker.hasChild(e.target) && e.target != this.visual);
		if (force || clickOutside) {
			this.mode = 'closed';
			if (this.options.useFadeInOut) {
				this.picker.set('tween', { duration: this.options.animationDuration / 2, onComplete: this.destroy.bind(this) }).tween('opacity', 1, 0);
			} else {
				this.destroy();
			}
		}

	},
	
	destroy: function() {
		this.picker.destroy();
		this.picker = null;
		if (typeof this.options.onClose == 'function')
			this.options.onClose();
		else if (typeof this.options.onClose == 'string')
			eval(this.options.onClose)
		this.mode = 'destroyed';
	},

	select: function(values) {
		this.choice = $merge(this.choice, values);
		var d = this.dateFromObject(this.choice);
		this.input.set('value', this.format(d, this.options.inputOutputFormat));
		this.visual.set('value', this.format(d, this.options.format));
		if (typeof this.options.onSelect == 'function')
			this.options.onSelect(d);
		else if (typeof this.options.onSelect == 'string')
			eval(this.options.onSelect)
		this.close(null, true);
	},
	leadZero: function(v) {
		return v < 10 ? '0'+v : v;
	},
	format: function(t, format) {
		var f = '';
		var h = t.getHours();
		var m = t.getMonth();
		
		for (var i = 0; i < format.length; i++) {
			switch(format.charAt(i)) {
				case '\\': i++; f+= format.charAt(i); break;
				case 'y': f += (100 + t.getYear() + '').substring(1); break
				case 'Y': f += t.getFullYear(); break;
				case 'm': f += this.leadZero(m + 1); break;
				case 'n': f += (m + 1); break;
				case 'M': f += this.options.months[m].substring(0,this.options.monthShort); break;
				case 'F': f += this.options.months[m]; break;
				case 'd': f += this.leadZero(t.getDate()); break;
				case 'j': f += t.getDate(); break;
				case 'D': f += this.options.days[t.getDay()].substring(0,this.options.dayShort); break;
				case 'l': f += this.options.days[t.getDay()]; break;
				case 'G': f += h; break;
				case 'H': f += this.leadZero(h); break;
				case 'g': f += (h % 12 ? h % 12 : 12); break;
				case 'h': f += this.leadZero(h % 12 ? h % 12 : 12); break;
				case 'a': f += (h > 11 ? 'pm' : 'am'); break;
				case 'A': f += (h > 11 ? 'PM' : 'AM'); break;
				case 'i': f += this.leadZero(t.getMinutes()); break;
				case 's': f += this.leadZero(t.getSeconds()); break;
				case 'U': f += Math.floor(t.valueOf() / 1000); break;
				default:  f += format.charAt(i);
			}
		}
		return f;
	},
	
	unformat: function(t, format) {
		var d = new Date();
		var a = {};
		var c, m;
		t = t.toString();
		
		for (var i = 0; i < format.length; i++) {
			c = format.charAt(i);
			switch(c) {
				case '\\': r = null; i++; break;
				case 'y': r = '[0-9]{2}'; break;
				case 'Y': r = '[0-9]{4}'; break;
				case 'm': r = '0[1-9]|1[012]'; break;
				case 'n': r = '[1-9]|1[012]'; break;
				case 'M': r = '[A-Za-z]{'+this.options.monthShort+'}'; break;
				case 'F': r = '[A-Za-z]+'; break;
				case 'd': r = '0[1-9]|[12][0-9]|3[01]'; break;
				case 'j': r = '[1-9]|[12][0-9]|3[01]'; break;
				case 'D': r = '[A-Za-z]{'+this.options.dayShort+'}'; break;
				case 'l': r = '[A-Za-z]+'; break;
				case 'G': 
				case 'H': 
				case 'g': 
				case 'h': r = '[0-9]{1,2}'; break;
				case 'a': r = '(am|pm)'; break;
				case 'A': r = '(AM|PM)'; break;
				case 'i': 
				case 's': r = '[012345][0-9]'; break;
				case 'U': r = '-?[0-9]+$'; break;
				default:  r = null;
			}
			
			if ($chk(r)) {
				m = t.match('^'+r);
				if ($chk(m)) {
					a[c] = m[0];
					t = t.substring(a[c].length);
				} else {
					if (this.options.debug) alert("Fatal Error in DatePicker\n\nUnexpected format at: '"+t+"' expected format character '"+c+"' (pattern '"+r+"')");
					return d;
				}
			} else {
				t = t.substring(1);
			}
		}
		
		for (c in a) {
			var v = a[c];
			switch(c) {
				case 'y': d.setFullYear(v < 30 ? 2000 + v.toInt() : 1900 + v.toInt()); break; // assume between 1930 - 2029
				case 'Y': d.setFullYear(v); break;
				case 'm':
				case 'n': d.setMonth(v - 1); break;
				// FALL THROUGH NOTICE! "M" has no break, because "v" now is the full month (eg. 'February'), which will work with the next format "F":
				case 'M': v = this.options.months.filter(function(item, index) { return item.substring(0,this.options.monthShort) == v }.bind(this))[0];
				case 'F': d.setMonth(this.options.months.indexOf(v)); break;
				case 'd':
				case 'j': d.setDate(v); break;
				case 'G': 
				case 'H': d.setHours(v); break;
				case 'g': 
				case 'h': if (a['a'] == 'pm' || a['A'] == 'PM') { d.setHours(v == 12 ? 0 : v.toInt() + 12); } else { d.setHours(v); } break;
				case 'i': d.setMinutes(v); break;
				case 's': d.setSeconds(v); break;
				case 'U': d = new Date(v.toInt() * 1000);
			}
		};
		
		return d;
	}
});


// comments are translated, may be incorrect
var DatePicker = new Class({	
	Implements: Options,
	
	// working date, which we will keep modifying to render the calendars
	d: '',
	
	// just so that we need not request it over and over
	today: '',
	
	// current user-choice in date object format
	choice: {}, 
	
	// size of body, used to animate the sliding
	bodysize: {}, 
	
	// to check availability of next/previous buttons
	limit: {}, 
	
	// element references:
	attachTo: null,    // selector for target inputs
	picker: null,      // main datepicker container
	slider: null,      // slider that contains both oldContents and newContents, used to animate between 2 different views
	oldContents: null, // used in animating from-view to new-view
	newContents: null, // used in animating from-view to new-view
	input: null,       // original input element (used for input/output)
	visual: null,      // visible input (used for rendering)
	
	options: { 
		pickerClass: 'datepicker',
		days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
		months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
		dayShort: 2,
		monthShort: 3,
		startDay: 1, // Sunday (0) through Saturday (6) - be aware that this may affect your layout, since the days on the right might have a different margin
		timePicker: false,
		timePickerOnly: false,
		yearPicker: true,
		yearsPerPage: 20,
		format: 'd-m-Y',
		allowEmpty: true,
		inputOutputFormat: 'U', // default to unix timestamp
		animationDuration: 400,
		useFadeInOut: !Browser.Engine.trident, // dont animate fade-in/fade-out for IE
		startView: 'month', // allowed values: {time, month, year, decades}
		positionOffset: { x: 0, y: 0 },
		minDate: null, // { date: '[date-string]', format: '[date-string-interpretation-format]' }
		maxDate: null, // same as minDate
		debug: false,
		toggleElements: null,
		
		// and some event hooks:
		onShow: $empty,   // triggered when the datepicker pops up
		onClose: $empty,  // triggered after the datepicker is closed (destroyed)
		onSelect: $empty  // triggered when a date is selected
	},

	initialize: function(attachTo, options) {
		this.attachTo = attachTo;
		this.setOptions(options).attach();
		if (this.options.timePickerOnly) {
			this.options.timePicker = true;
			this.options.startView = 'time';
		}
		this.formatMinMaxDates();
		document.addEvent('mousedown', this.close.bind(this));
	},
	
	formatMinMaxDates: function() {
		if (this.options.minDate && this.options.minDate.format) {
			this.options.minDate = this.unformat(this.options.minDate.date, this.options.minDate.format);
		}
		if (this.options.maxDate && this.options.maxDate.format) {
			this.options.maxDate = this.unformat(this.options.maxDate.date, this.options.maxDate.format);
			this.options.maxDate.setHours(23);
			this.options.maxDate.setMinutes(59);
			this.options.maxDate.setSeconds(59);
		}
	},
	
	attach: function() {
		// toggle the datepicker through a separate element?
		if ($chk(this.options.toggleElements)) {
			var togglers = $$(this.options.toggleElements);
			document.addEvents({
				'keydown': function(e) {
					if (e.key == "tab") {
						this.close(null, true);
					}
				}.bind(this)
			});
		};
		
		// attach functionality to the inputs		
		$$(this.attachTo).each(function(item, index) {
			
			// never double attach
			if (item.retrieve('datepicker')) return;

			// determine starting value(s)
			if ($chk(item.get('value'))) {
				var init_clone_val = this.format(new Date(this.unformat(item.get('value'), this.options.inputOutputFormat)), this.options.format);
			} else if (!this.options.allowEmpty) {
				var init_clone_val = this.format(new Date(), this.options.format);
			} else {
				var init_clone_val = '';
			}
			
			// create clone
			var display = item.getStyle('display');
			var clone = item
			.setStyle('display', this.options.debug ? display : 'none')
			.store('datepicker', true) // to prevent double attachment...
			.clone()
			.store('datepicker', true) // ...even for the clone (!)
			.removeProperty('name')    // secure clean (form)submission
			.setStyle('display', display)
			.set('value', init_clone_val)
			.inject(item, 'after');
			
			// events
			if ($chk(this.options.toggleElements)) {
				togglers[index]
					.setStyle('cursor', 'pointer')
					.addEvents({
						'click': function(e) {
							this.onFocus(item, clone);
						}.bind(this)
					});
				clone.addEvents({
					'blur': function() {
						item.set('value', clone.get('value'));
					}
				});
			} else {
				clone.addEvents({
					'keydown': function(e) {
						if (this.options.allowEmpty && (e.key == "delete" || e.key == "backspace")) {
							item.set('value', '');
							e.target.set('value', '');
							this.close(null, true);
						} else if (e.key == "tab") {
							this.close(null, true);
						} else {
							e.stop();
						}
					}.bind(this),
					'focus': function(e) {
						this.onFocus(item, clone);
					}.bind(this)
				});
			}
		}.bind(this));
	},
	
	onFocus: function(original_input, visual_input) {
		var init_visual_date, d = visual_input.getCoordinates();
		
		if ($chk(original_input.get('value'))) {
			init_visual_date = this.unformat(original_input.get('value'), this.options.inputOutputFormat).valueOf();
		} else {
			init_visual_date = new Date();
			if ($chk(this.options.maxDate) && init_visual_date.valueOf() > this.options.maxDate.valueOf()) {
				init_visual_date = new Date(this.options.maxDate.valueOf());
			}
			if ($chk(this.options.minDate) && init_visual_date.valueOf() < this.options.minDate.valueOf()) {
				init_visual_date = new Date(this.options.minDate.valueOf());
			}
		}
		
		this.show({ left: d.left + this.options.positionOffset.x, top: d.top + d.height + this.options.positionOffset.y }, init_visual_date);
		this.input = original_input;
		this.visual = visual_input;
		this.options.onShow();
	},
	
	dateToObject: function(d) {
		return {
			year: d.getFullYear(),
			month: d.getMonth(),
			day: d.getDate(),
			hours: d.getHours(),
			minutes: d.getMinutes(),
			seconds: d.getSeconds()
		};
	},
	
	dateFromObject: function(values) {
		var d = new Date();
		d.setDate(1);
		['year', 'month', 'day', 'hours', 'minutes', 'seconds'].each(function(type) {
			var v = values[type];
			if (!$chk(v)) return;
			switch (type) {
				case 'day': d.setDate(v); break;
				case 'month': d.setMonth(v); break;
				case 'year': d.setFullYear(v); break;
				case 'hours': d.setHours(v); break;
				case 'minutes': d.setMinutes(v); break;
				case 'seconds': d.setSeconds(v); break;
			}
		});
		return d;
	},
	
	show: function(position, timestamp) {
		this.formatMinMaxDates();
		if ($chk(timestamp)) {
			this.d = new Date(timestamp);
		} else {
			this.d = new Date();
		}
		this.today = new Date();
		this.choice = this.dateToObject(this.d);
		this.mode = (this.options.startView == 'time' && !this.options.timePicker) ? 'month' : this.options.startView;
		this.render();
		this.picker.setStyles(position);
	},
	
	render: function(fx) {
		if (!$chk(this.picker)) {
			this.constructPicker();
		} else {
			// swap contents so we can fill the newContents again and animate
			var o = this.oldContents;
			this.oldContents = this.newContents;
			this.newContents = o;
			this.newContents.empty();
		}
		
		// remember current working date
		var startDate = new Date(this.d.getTime());
		
		// intially assume both left and right are allowed
		this.limit = { right: false, left: false };
		
		// render! booty!
		if (this.mode == 'decades') {
			this.renderDecades();
		} else if (this.mode == 'year') {
			this.renderYear();
		} else if (this.mode == 'time') {
			this.renderTime();
			this.limit = { right: true, left: true }; // no left/right in timeview
		} else {
			this.renderMonth();
		}
		
		this.picker.getElement('.previous').setStyle('visibility', this.limit.left ? 'hidden' : 'visible');
		this.picker.getElement('.next').setStyle('visibility', this.limit.right ? 'hidden' : 'visible');
		this.picker.getElement('.titleText').setStyle('cursor', this.allowZoomOut() ? 'pointer' : 'default');
		
		// restore working date
		this.d = startDate;
		
		// if ever the opacity is set to '0' it was only to have us fade it in here
		// refer to the constructPicker() function, which instantiates the picker at opacity 0 when fading is desired
		if (this.picker.getStyle('opacity') == 0) {
			this.picker.tween('opacity', 0, 1);
		}
		
		// animate
		if ($chk(fx)) this.fx(fx);
	},
	
	fx: function(fx) {
		if (fx == 'right') {
			this.oldContents.setStyles({ left: 0, opacity: 1 });
			this.newContents.setStyles({ left: this.bodysize.x, opacity: 1 });
			this.slider.setStyle('left', 0).tween('left', 0, -this.bodysize.x);
		} else if (fx == 'left') {
			this.oldContents.setStyles({ left: this.bodysize.x, opacity: 1 });
			this.newContents.setStyles({ left: 0, opacity: 1 });
			this.slider.setStyle('left', -this.bodysize.x).tween('left', -this.bodysize.x, 0);
		} else if (fx == 'fade') {
			this.slider.setStyle('left', 0);
			this.oldContents.setStyle('left', 0).set('tween', { duration: this.options.animationDuration / 2 }).tween('opacity', 1, 0);
			this.newContents.setStyles({ opacity: 0, left: 0}).set('tween', { duration: this.options.animationDuration }).tween('opacity', 0, 1);
		}
	},
	
	constructPicker: function() {
		this.picker = new Element('div', { 'class': this.options.pickerClass }).inject(document.body);
		if (this.options.useFadeInOut) {
			this.picker.setStyle('opacity', 0).set('tween', { duration: this.options.animationDuration });
		}
		
		var h = new Element('div', { 'class': 'header' }).inject(this.picker);
		var titlecontainer = new Element('div', { 'class': 'title' }).inject(h);
		new Element('div', { 'class': 'previous' }).addEvent('click', this.previous.bind(this)).set('text', '«').inject(h);
		new Element('div', { 'class': 'next' }).addEvent('click', this.next.bind(this)).set('text', '»').inject(h);
		new Element('div', { 'class': 'closeButton' }).addEvent('click', this.close.bindWithEvent(this, true)).set('text', 'x').inject(h);
		new Element('span', { 'class': 'titleText' }).addEvent('click', this.zoomOut.bind(this)).inject(titlecontainer);
		
		var b = new Element('div', { 'class': 'body' }).inject(this.picker);
		this.bodysize = b.getSize();
		this.slider = new Element('div', { styles: { position: 'absolute', top: 0, left: 0, width: 2 * this.bodysize.x, height: this.bodysize.y }})
					.set('tween', { duration: this.options.animationDuration, transition: Fx.Transitions.Quad.easeInOut }).inject(b);
		this.oldContents = new Element('div', { styles: { position: 'absolute', top: 0, left: this.bodysize.x, width: this.bodysize.x, height: this.bodysize.y }}).inject(this.slider);
		this.newContents = new Element('div', { styles: { position: 'absolute', top: 0, left: 0, width: this.bodysize.x, height: this.bodysize.y }}).inject(this.slider);
	},
	
	renderTime: function() {
		var container = new Element('div', { 'class': 'time' }).inject(this.newContents);
		
		if (this.options.timePickerOnly) {
			this.picker.getElement('.titleText').set('text', 'Select a time');
		} else {
			this.picker.getElement('.titleText').set('text', this.format(this.d, 'j M, Y'));
		}

		in_pm = false;
		var curr_hours = this.d.getHours();

		if (curr_hours >= 12)
			in_pm = true;
		if (curr_hours > 12) {
			curr_hours -= 12;
		}
		
		if (curr_hours == 0)
			curr_hours = 12;

		new Element('input', { type: 'text', 'class': 'hour' })
			.set('value', this.leadZero(curr_hours))
			.addEvents({
				mousewheel: function(e) {
					var i = e.target, v = i.get('value').toInt();
					i.focus();
					if (e.wheel > 0) {
						v = (v < 12) ? v + 1 : 1;
					} else {
						v = (v > 1) ? v - 1 : 12;
					}
					i.set('value', this.leadZero(v));
					e.stop();
				}.bind(this)
			})
			.set('maxlength', 2)
			.inject(container);
			
		var tb = new Element('input', { type: 'text', 'class': 'minutes' })
			.set('value', this.leadZero(this.d.getMinutes()))
			.addEvents({
				mousewheel: function(e) {
					var i = e.target, v = i.get('value').toInt();
					i.focus();
					if (this.input.hasClass('timeBlocks')) {					
						if (e.wheel > 0) {
							v = (v < 45) ? v + 15 : 0;
						} else {
							v = (v > 0) ? v - 15 : 45;
						}
					} else {					
						if (e.wheel > 0) {
							v = (v < 59) ? v + 1 : 0;
						} else {
							v = (v > 0) ? v - 1 : 59;
						}
					}
					i.set('value', this.leadZero(v));
					e.stop();
				}.bind(this)
			})
			.set('maxlength', 2)
			.inject(container);
		
		tb.set('value', this.leadZero(0));


		new Element('input', { id:'am_ele', type: 'button', 'class': 'timeblock am'})
			.addEvents({
				click: function(e) {
					var i = e.target;
					var tclass = i.className;

					if (tclass.indexOf('selected') != -1)
						return false;

					$('pm_ele').removeClass('selected');
					i.addClass('selected');

					e.stop();
				}.bind(this)
			}).inject(container).set('value', 'AM');
			
		new Element('input', { id:'pm_ele', type: 'button', 'class': 'timeblock pm'})
			.addEvents({
				click: function(e) {
					var i = e.target;
					var tclass = e.target.className;

					if (tclass.indexOf('selected') != -1)
						return false;

					$('am_ele').removeClass('selected');
					i.addClass('selected');

					e.stop();
				}.bind(this)
			}).inject(container).set('value', 'PM');



		if (in_pm == false)
			$('am_ele').addClass('selected');
		else
			$('pm_ele').addClass('selected');




		new Element('div', { 'class': 'separator' }).set('text', ':').inject(container);
		
		new Element('div', { 'class': 'separator' }).set('text', ':').inject(container);
		
		new Element('input', { type: 'submit', value: 'OK', 'class': 'ok' })
			.addEvents({
				click: function(e) {
					e.stop();
					var ampm = (($('pm_ele').className.indexOf('selected') != -1) ? 'pm' : 'am');
					am_pm_boost = 0;
					if (ampm == 'pm' && this.picker.getElement('.hour').get('value').toInt() != 12) {
						am_pm_boost = 12;
					} else if (ampm == 'am' && this.picker.getElement('.hour').get('value').toInt() == 12) {
						am_pm_boost = -12;
					}
					this.select($merge(this.dateToObject(this.d), { hours: this.picker.getElement('.hour').get('value').toInt() + am_pm_boost, minutes: this.picker.getElement('.minutes').get('value').toInt() }));
				}.bind(this)
			})
			.set('maxlength', 2)
			.inject(container);
	},
	
	renderMonth: function() {
		var month = this.d.getMonth();
		
		this.picker.getElement('.titleText').set('text', this.options.months[month] + ' ' + this.d.getFullYear());
		
		this.d.setDate(1);
		while (this.d.getDay() != this.options.startDay) {
			this.d.setDate(this.d.getDate() - 1);
		}
		
		var container = new Element('div', { 'class': 'days' }).inject(this.newContents);
		var titles = new Element('div', { 'class': 'titles' }).inject(container);
		var d, i, classes, e, weekcontainer;

		for (d = this.options.startDay; d < (this.options.startDay + 7); d++) {
			new Element('div', { 'class': 'title day day' + (d % 7) }).set('text', this.options.days[(d % 7)].substring(0,this.options.dayShort)).inject(titles);
		}
		
		var available = false;
		var t = this.today.toDateString();
		var currentChoice = this.dateFromObject(this.choice).toDateString();
		
		for (i = 0; i < 42; i++) {
			classes = [];
			classes.push('day');
			classes.push('day'+this.d.getDay());
			if (this.d.toDateString() == t) classes.push('today');
			if (this.d.toDateString() == currentChoice) classes.push('selected');
			if (this.d.getMonth() != month) classes.push('otherMonth');
			
			if (i % 7 == 0) {
				weekcontainer = new Element('div', { 'class': 'week week'+(Math.floor(i/7)) }).inject(container);
			}
			
			e = new Element('div', { 'class': classes.join(' ') }).set('text', this.d.getDate()).inject(weekcontainer);
			if (this.limited('date')) {
				e.addClass('unavailable');
				if (available) {
					this.limit.right = true;
				} else if (this.d.getMonth() == month) {
					this.limit.left = true;
				}
			} else {
				available = true;
				e.addEvent('click', function(e, d) {
					if (this.options.timePicker) {
						this.d.setDate(d.day);
						this.d.setMonth(d.month);
						this.mode = 'time';
						this.render('fade');
					} else {
						this.select(d);
					}
				}.bindWithEvent(this, { day: this.d.getDate(), month: this.d.getMonth(), year: this.d.getFullYear() }));
			}
			this.d.setDate(this.d.getDate() + 1);
		}
		if (!available) this.limit.right = true;
		
		if (this.options.monthPicker) {
			var g = {'day': '1', 'month': this.d.getMonth() , 'year' : this.d.getFullYear()};
			this.select(g);
			return;
		}
		
		
	},
	
	renderYear: function() {
		var month = this.today.getMonth();
		var thisyear = this.d.getFullYear() == this.today.getFullYear();
		var selectedyear = this.d.getFullYear() == this.choice.year;
		
		this.picker.getElement('.titleText').set('text', this.d.getFullYear());
		this.d.setMonth(0);
		
		var i, e;
		var available = false;
		var container = new Element('div', { 'class': 'months' }).inject(this.newContents);
		
		for (i = 0; i <= 11; i++) {
			e = new Element('div', { 'class': 'month month'+(i+1)+(i == month && thisyear ? ' today' : '')+(i == this.choice.month && selectedyear ? ' selected' : '') })
			.set('text', this.options.monthShort ? this.options.months[i].substring(0, this.options.monthShort) : this.options.months[i]).inject(container);
			
			if (this.limited('month')) {
				e.addClass('unavailable');
				if (available) {
					this.limit.right = true;
				} else {
					this.limit.left = true;
				}
			} else {
				available = true;
				e.addEvent('click', function(e, d) {
					this.d.setDate(1);
					this.d.setMonth(d);
					this.mode = 'month';
					this.render('fade');
				}.bindWithEvent(this, i));
			}
			this.d.setMonth(i);
		}
		if (!available) this.limit.right = true;
	},
	
	renderDecades: function() {
		// start neatly at interval (eg. 1980 instead of 1987)
		while (this.d.getFullYear() % this.options.yearsPerPage > 0) {
			this.d.setFullYear(this.d.getFullYear() - 1);
		}

		this.picker.getElement('.titleText').set('text', this.d.getFullYear() + '-' + (this.d.getFullYear() + this.options.yearsPerPage - 1));
		
		var i, y, e;
		var available = false;
		var container = new Element('div', { 'class': 'years' }).inject(this.newContents);
		
		if ($chk(this.options.minDate) && this.d.getFullYear() <= this.options.minDate.getFullYear()) {
			this.limit.left = true;
		}
		
		for (i = 0; i < this.options.yearsPerPage; i++) {
			y = this.d.getFullYear();
			e = new Element('div', { 'class': 'year year' + i + (y == this.today.getFullYear() ? ' today' : '') + (y == this.choice.year ? ' selected' : '') }).set('text', y).inject(container);
			
			if (this.limited('year')) {
				e.addClass('unavailable');
				if (available) {
					this.limit.right = true;
				} else {
					this.limit.left = true;
				}
			} else {
				available = true;
				e.addEvent('click', function(e, d) {
					this.d.setFullYear(d);
					this.mode = 'year';
					this.render('fade');
				}.bindWithEvent(this, y));
			}
			this.d.setFullYear(this.d.getFullYear() + 1);
		}
		if (!available) {
			this.limit.right = true;
		}
		if ($chk(this.options.maxDate) && this.d.getFullYear() >= this.options.maxDate.getFullYear()) {
			this.limit.right = true;
		}
	},
	
	limited: function(type) {
		var cs = $chk(this.options.minDate);
		var ce = $chk(this.options.maxDate);
		if (!cs && !ce) return false;
		
		switch (type) {
			case 'year':
				return (cs && this.d.getFullYear() < this.options.minDate.getFullYear()) || (ce && this.d.getFullYear() > this.options.maxDate.getFullYear());
				
			case 'month':
				// todo: there has got to be an easier way...?
				var ms = ('' + this.d.getFullYear() + this.leadZero(this.d.getMonth())).toInt();
				return cs && ms < ('' + this.options.minDate.getFullYear() + this.leadZero(this.options.minDate.getMonth())).toInt()
					|| ce && ms > ('' + this.options.maxDate.getFullYear() + this.leadZero(this.options.maxDate.getMonth())).toInt()
				
			case 'date':
				return (cs && this.d < this.options.minDate) || (ce && this.d > this.options.maxDate);
		}
	},
	
	allowZoomOut: function() {
		if (this.mode == 'time' && this.options.timePickerOnly) return false;
		if (this.mode == 'decades') return false;
		if (this.mode == 'year' && !this.options.yearPicker) return false;
		return true;
	},
	
	zoomOut: function() {
		if (!this.allowZoomOut()) return;
		if (this.mode == 'year') {
			this.mode = 'decades';
		} else if (this.mode == 'time') {
			this.mode = 'month';
		} else {
			this.mode = 'year';
		}
		this.render('fade');
	},
	
	previous: function() {
		if (this.mode == 'decades') {
			this.d.setFullYear(this.d.getFullYear() - this.options.yearsPerPage);
		} else if (this.mode == 'year') {
			this.d.setFullYear(this.d.getFullYear() - 1);
		} else if (this.mode == 'month') {
			this.d.setMonth(this.d.getMonth() - 1);
		}
		this.render('left');
	},
	
	next: function() {
		if (this.mode == 'decades') {
			this.d.setFullYear(this.d.getFullYear() + this.options.yearsPerPage);
		} else if (this.mode == 'year') {
			this.d.setFullYear(this.d.getFullYear() + 1);
		} else if (this.mode == 'month') {
			this.d.setMonth(this.d.getMonth() + 1);
		}
		this.render('right');
	},
	
	close: function(e, force) {
		if (!$(this.picker)) return;
		var clickOutside = ($chk(e) && e.target != this.picker && !this.picker.hasChild(e.target) && e.target != this.visual);
		if (force || clickOutside) {
			if (this.options.useFadeInOut) {
				this.picker.set('tween', { duration: this.options.animationDuration / 2, onComplete: this.destroy.bind(this) }).tween('opacity', 1, 0);
			} else {
				this.destroy();
			}
		}
	},
	
	destroy: function() {
		this.picker.destroy();
		this.picker = null;
		this.options.onClose();
	},
	
	select: function(values) {
	console.log('bull sh');
		this.choice = $merge(this.choice, values);
		var d = this.dateFromObject(this.choice);
		var updater_function = this.input.retrieve('update_event');
		if (typeof updater_function == 'function') {
			updater_function(this.input, d);
		}
		this.input.set('value', this.format(d, this.options.inputOutputFormat));
		this.visual.set('value', this.format(d, this.options.format));
		this.options.onSelect(d);
		this.close(null, true);
	},
	
	leadZero: function(v) {
		return v < 10 ? '0'+v : v;
	},
	
	format: function(t, format) {
		var f = '';
		var h = t.getHours();
		var m = t.getMonth();
		
		for (var i = 0; i < format.length; i++) {
			switch(format.charAt(i)) {
				case '\\': i++; f+= format.charAt(i); break;
				case 'y': f += (100 + t.getYear() + '').substring(1); break
				case 'Y': f += t.getFullYear(); break;
				case 'm': f += this.leadZero(m + 1); break;
				case 'n': f += (m + 1); break;
				case 'M': f += this.options.months[m].substring(0,this.options.monthShort); break;
				case 'F': f += this.options.months[m]; break;
				case 'd': f += this.leadZero(t.getDate()); break;
				case 'j': f += t.getDate(); break;
				case 'D': f += this.options.days[t.getDay()].substring(0,this.options.dayShort); break;
				case 'l': f += this.options.days[t.getDay()]; break;
				case 'G': f += h; break;
				case 'H': f += this.leadZero(h); break;
				case 'g': f += (h % 12 ? h % 12 : 12); break;
				case 'h': f += this.leadZero(h % 12 ? h % 12 : 12); break;
				case 'a': f += (h > 11 ? 'pm' : 'am'); break;
				case 'A': f += (h > 11 ? 'PM' : 'AM'); break;
				case 'i': f += this.leadZero(t.getMinutes()); break;
				case 's': f += this.leadZero(t.getSeconds()); break;
				case 'U': f += Math.floor(t.valueOf() / 1000); break;
				default:  f += format.charAt(i);
			}
		}
		return f;
	},
	
	unformat: function(t, format) {
		var d = new Date();
		var a = {};
		var c, m;
		t = t.toString();
		
		for (var i = 0; i < format.length; i++) {
			c = format.charAt(i);
			switch(c) {
				case '\\': r = null; i++; break;
				case 'y': r = '[0-9]{2}'; break;
				case 'Y': r = '[0-9]{4}'; break;
				case 'm': r = '0[1-9]|1[012]'; break;
				case 'n': r = '[1-9]|1[012]'; break;
				case 'M': r = '[A-Za-z]{'+this.options.monthShort+'}'; break;
				case 'F': r = '[A-Za-z]+'; break;
				case 'd': r = '0[1-9]|[12][0-9]|3[01]'; break;
				case 'j': r = '[1-9]|[12][0-9]|3[01]'; break;
				case 'D': r = '[A-Za-z]{'+this.options.dayShort+'}'; break;
				case 'l': r = '[A-Za-z]+'; break;
				case 'G': 
				case 'H': 
				case 'g': 
				case 'h': r = '[0-9]{1,2}'; break;
				case 'a': r = '(am|pm)'; break;
				case 'A': r = '(AM|PM)'; break;
				case 'i': 
				case 's': r = '[012345][0-9]'; break;
				case 'U': r = '-?[0-9]+$'; break;
				default:  r = null;
			}
			
			if ($chk(r)) {
				m = t.match('^'+r);
				if ($chk(m)) {
					a[c] = m[0];
					t = t.substring(a[c].length);
				} else {
					if (this.options.debug) alert("Fatal Error in DatePicker\n\nUnexpected format at: '"+t+"' expected format character '"+c+"' (pattern '"+r+"')");
					return d;
				}
			} else {
				t = t.substring(1);
			}
		}
		
		for (c in a) {
			var v = a[c];
			switch(c) {
				case 'y': d.setFullYear(v < 30 ? 2000 + v.toInt() : 1900 + v.toInt()); break; // assume between 1930 - 2029
				case 'Y': d.setFullYear(v); break;
				case 'm':
				case 'n': d.setMonth(v - 1); break;
				// FALL THROUGH NOTICE! "M" has no break, because "v" now is the full month (eg. 'February'), which will work with the next format "F":
				case 'M': v = this.options.months.filter(function(item, index) { return item.substring(0,this.options.monthShort) == v }.bind(this))[0];
				case 'F': d.setMonth(this.options.months.indexOf(v)); break;
				case 'd':
				case 'j': d.setDate(v); break;
				case 'G': 
				case 'H': d.setHours(v); break;
				case 'g': 
				case 'h': if (a['a'] == 'pm' || a['A'] == 'PM') { d.setHours(v == 12 ? 0 : v.toInt() + 12); } else { d.setHours(v); } break;
				case 'i': d.setMinutes(v); break;
				case 's': d.setSeconds(v); break;
				case 'U': d = new Date(v.toInt() * 1000);
			}
		};
		
		return d;
	}
});




/* END site/timePicker
*************************************************************/

/**************************************************************
 BEGIN site/multi                                           */
/*
---
description:     
  - MultiSelect is a MooTools plugin that turns your checkbox set into one single multi-select dropdown menu. MultiSelect is also completely CSS skinnable.

authors:
  - Blaž Maležic (http://twitter.com/blazmalezic)

version:
  - 1.3.1

license:
  - MIT-style license

requires:
  core/1.2.1:   '*'

provides:
  - MultiSelect
...
*/
var MultiSelect = new Class({
	Implements: [Options], 
	options: {
		boxes: 'input[type=checkbox]', 	// checkbox selector
		labels: 'label', 				// label selector
		monitorAppend: ' selected',		// monitor text (localization)
		monitorPrepend: '',		// monitor text (localization)
		closerText: 'close X',
		monitorClass: 'monitor',		// monitor CSS class
		monitorActiveClass: 'active',	// monitor open CSS class
		itemSelectedClass: 'selected',	// list item selected CSS class
		itemHoverClass: 'hover',			// list item hover CSS class - usually we would use CSS :hover pseudo class, but we need this for keyboard navigation functionality
		onClose: false,
		onOpen: false,
		onChange: false
	}, 
	
	initialize: function(selector, options) {
		// set options
		this.setOptions(options);
		
		// set global action variables
		this.active = false;
		this.action = 'open';
		this.state = 'closed';
		
		this.boxes = {};
		
		// get elements array
		this.element = $(selector);
		
		// off we go...
		this.buildMenu(this.element);
	},
	
	buildMenu: function(element) {
		// create closure
		var self = this;
		
		// create item instances
		this.boxes = element.getElements(self.options.boxes);
		var labels = element.getElements(self.options.labels);
		
		// list container
		var list = new Element('ul', {
			'styles': { display: 'none' },
			'events': {
				'mouseenter': function() { self.action = 'open'; }, 
				'mouseleave': function() { 
					self.action = 'close';
					self.itemHover(this, 'none');
				},
				
				'mousedown': function(e) { e.stop(); }, // stop text selection
				'selectstart': function() { return false; }, // stop IE text selection
				
				'keydown': function(e) {
					if (e.key == 'esc') {
						self.toggleMenu('close', monitor, this);
					}
					else if (e.key == 'down' || e.key == 'up') {
						self.itemHover(this, e.key);
					}
				}
			}
		});
		// list items
		this.boxes.each(function(box, i) {
			box.addEvents({
				'click': function(e) {
					e.stop();
				},
				'keydown': function(e) {
					if (e.key == 'space') {
						self.active = true;
						self.changeItemState(this.getParent(), this, monitor);
					}
					if (self.active && (e.key == 'down' || e.key == 'up')) {
						self.changeItemState(this.getParent(), this, monitor);
					}
				},
				'keyup': function(e) {
					if (e.key == 'space') {
						self.active = false;
					}
				}
			});
			var label = labels[i];
			new Element('li', {
				'class': box.get('checked') ? self.options.itemSelectedClass : '',
				'events': {
					'mouseenter': function() {
						if (self.active === true) {
							self.changeItemState(this, box, monitor);
						}
						self.itemHover(list, this);
					},
					'mousedown': function() {
						self.active = true;
						self.changeItemState(this, box, monitor);
					}
				}
			}).adopt([box, label]).inject(list);
		});
		// list monitor
		var monitor = new Element('div', {
			'class': self.options.monitorClass,
			'html': '<div class="activator" style="float:left;">' + self.changeMonitorValue(list) + '</div><div class="close_notice" style="float:right; display:none;">'+self.options.closerText+'</div>',
			'tabindex': 0,
			'events': {
				'mouseenter': function() { self.action = 'open'; }, 
				'mouseleave': function() { self.action = 'close'; },
				'click': function() { 
					
					if (this.hasClass(self.options.monitorActiveClass)) {
						self.toggleMenu('close', monitor, list);
					}
					else {
						self.toggleMenu('open', monitor, list);
					}
				},
				'keydown': function(e) {
					if (e.key == 'space' || e.key == 'down' || e.key == 'up') {
						self.action = 'close';
						self.toggleMenu('open', monitor, list);
					}
				},
				
				'mousedown': function(e) { e.stop(); }, // stop text selection
				'selectstart': function() { return false; } // stop IE text selection
			}
		});
		// 'global' events
		document.addEvents({
			'mouseup': function() { self.active = false; },
			'click': function() {
				if (self.action == 'close') {
					self.toggleMenu('close', monitor, list);
				}
			},
			'keydown': function(e) {
				if (e.key == 'esc') {
					self.toggleMenu('close', monitor, list);
					self.itemHover(list, 'none');
				}
				if (self.state == 'opened' && (e.key == 'down' || e.key == 'up')) {
					e.stop();
				}
			}
		});
		// replace element content
		element.empty().adopt([monitor, list]);
	}, 
	
	getValues: function () {
		values = [];
		$each (this.boxes, function (e) {
			if (e.checked)
				values.include(e.value);
		}, this);
		return values;
	},

	
	changeItemState: function(item, checkbox, monitor) {
		if (item.hasClass(this.options.itemSelectedClass)) {
			item.removeClass(this.options.itemSelectedClass);
			checkbox.set('checked', false).focus();
		}
		else {
			item.addClass(this.options.itemSelectedClass);
			checkbox.set('checked', true).focus();
		}
		
		if (this.options.onChange && typeof this.options.onChange == 'function')
			this.options.onChange(this);

		monitor.set('html', '<div class="activator" style="float:left;">' + this.changeMonitorValue(item.getParent()) + '</div><div class="close_notice" style="float:right;">'+this.options.closerText+'</div>');
	}, 

	changeMonitorValue: function(list) {
		var text = this.options.monitorPrepend + list.getElements(this.options.boxes).filter(function(box) {
			return box.get('checked');
		}).length + this.options.monitorAppend;
		
		return text;
	}, 
	
	itemHover: function(list, select) {
		var current = list.getElement('li.'+this.options.itemHoverClass);
		
		switch (select) {
			case 'down':
				if (current && (sibling = current.getNext())) current.removeClass(this.options.itemHoverClass);
				else this.itemHover(list, 'last');
				break;
 			case 'up':
				if (current && (sibling = current.getPrevious())) current.removeClass(this.options.itemHoverClass);
				else this.itemHover(list, 'first');
				break;
			case 'none':
				list.getElements('li.'+this.options.itemHoverClass).removeClass(this.options.itemHoverClass);
				break;
			case 'first':
				var sibling = list.getFirst();
				break;
			case 'last':
				var sibling = list.getLast();
				break;
			default:
				if (current) current.removeClass(this.options.itemHoverClass);
				var sibling = select;
				break;
		}
		
		if (sibling) 
			sibling.addClass(this.options.itemHoverClass).getElement(this.options.boxes).focus();
	},
	
	toggleMenu: function(toggle, monitor, list) {
		if (toggle == 'open') {
			monitor.addClass(this.options.monitorActiveClass);
			list.setStyle('display', '');
			this.itemHover(list, 'first');
			monitor.getElement('div[class=close_notice]').show();
			this.state = 'opened';
			if (this.options.onOpen && typeof this.options.onOpen == 'function')
				this.options.onOpen(this);
		}
		else {
			// close all MultiSelect menus
			this.element.getElement('div.monitor').removeClass(this.options.monitorActiveClass);
			this.element.getElement('ul').setStyle('display', 'none');
			monitor.getElement('div[class=close_notice]').hide();
			this.action = 'open';
			this.state = 'closed';
			if (this.options.onClose && typeof this.options.onClose == 'function')
				this.options.onClose(this);
		}
		
		if (list.getScrollSize().y > (list.getStyle('max-height').toInt() ? list.getStyle('max-height').toInt() : list.getStyle('height').toInt()))
			list.setStyle('overflow-y', 'scroll');
	}
});

/* END site/multi
*************************************************************/

/**************************************************************
 BEGIN component/global_search_bar                                           */
//<script>

var SearchBar;
window.addEvent('domready', function () {
	SearchBar = search_bar;
	SearchBar.init();
})


function do_event_search() {
	var cat_id = $$('#search_bar #search_cat')[0].get('value');
	var search_term = $$('#search_bar #search_term')[0].get('value');
	var date_range = $$('#search_bar #search_range')[0].get('value');
	var type = $$('#search_bar #search_type_switch')[0].get('html');
	goto(URL+'index.php?page=search&t='+type+'&c='+cat_id+'&q='+search_term+'&d='+date_range);
	return false;
}


var search_bar = {

	init : function () {
		this.tab = $('search_tab');
		this.bar = $('search_bar');
		this.options_container = $('search_options');
		this.state = 'closed';
		this.options_state = 'closed';
		this.options_display = $('search_type_switch');
		this.term = $('search_term');
		this.layout = $('search_layout');
		
		$('search_btn').addEvent('click', function() {
			do_event_search();
			return false;
		})
		$('search_tab').addEvent('click', function () {
			SearchBar.toggle_search();	
		});
		this.options_display.addEvent('click', this.toggle_options);
	
		this.search_target = $('search_start_opt').get('html');
		this.options_display.set('html', this.search_target);
	
		$('search_optn_news').addEvent('click', this.switch_search_target);
		$('search_optn_news').set('rel', 'news');
		if (this.search_target == 'news') {
			$('search_optn_news').addClass('orange').removeClass('white');
			this.search_target_link = $('search_optn_news');
		}
		$('search_optn_events').addEvent('click', this.switch_search_target);
		$('search_optn_events').set('rel', 'events');
		if (this.search_target == 'events') {
			$('search_optn_events').addClass('orange').removeClass('white');
			this.search_target_link = $('search_optn_events');
		}
		
		$('search_optn_all').addEvent('click', this.switch_search_target);
		$('search_optn_all').set('rel', 'all');
		if (this.search_target == 'all') {
			$('search_optn_all').addClass('orange').removeClass('white');
			this.search_target_link = $('search_optn_all');
		}
		
		this.term.addEvent('click', this.toggle_options);
		this.term.addEvent('keydown', function (e) {
			if(SearchBar.options_container.getSize().y > 0) {
				SearchBar.options_container.tween('height', '0px').hide();
				SearchBar.options_state = 'closed';
			}
			
			if (e.event.keyCode == 13) {
				do_event_search();
			}
		});
		
	},
	term_focus : function () {
		
		SearchBar.layout.show();
		SearchBar.term.focus();
	},
	toggle_search : function (force) {
		console.log('here:'+SearchBar.state);
		if (SearchBar.state == 'closed' || force == 'open') {
			SearchBar.layout.hide();
			console.log('tweening:')
			console.log(SearchBar.bar)
			SearchBar.bar.get('tween', {property: 'height', duration: 'short'});
			SearchBar.bar.get('tween').addEvent('complete', SearchBar.term_focus).start('48px');
			SearchBar.state = 'open';
			setTimeout('SearchBar.set_auto_close()', 1000);
			if (SearchBar.layout.getPosition().y < 0) {
				SearchBar.layout.setStyle('top', (SearchBar.layout.getPosition().y * -1) +'px');
			}

		} else if (SearchBar.state == 'open' || force == 'closed') {
			SearchBar.bar.tween('height', '0px');
			SearchBar.state = 'closed';
			SearchBar.layout.hide();
			SearchBar.toggle_options(true);
			$('header').removeEvent('click', SearchBar.do_auto_close);
			$('content').removeEvent('click', SearchBar.do_auto_close);
		}
	},
	

	reload_cats : function () {
		jsonreq('global_search_bar', 'action=search_cats&type='+this.search_target, 'SearchBar.reload_cats_callback');
	},
	
	

	reload_cats_callback : function (response) {
		$('search_cat').options.length = 0;
		var v = 0;

		var nOpt = new Option('select category', '0');
		$('search_cat').options[v] = nOpt;
		v++;
		
		var nOpt = new Option('any', '0');
		$('search_cat').options[v] = nOpt;
		v++;
		
		for (var g=0; g<response.length; g++) {
			var nOpt = new Option(response[g].name, response[g].id);
			$('search_cat').options[v] = nOpt;
			v++;
		}
		
		
		
	},
	
	
	
	
	
	toggle_options : function (force) {	
		if (typeof force == 'undefined' || typeof force != 'boolean')
			force = false;

		if (SearchBar.options_state == 'closed' && !force) {
			SearchBar.options_container.show().tween('height', '74px');
			SearchBar.options_state = 'open';
		} else {
			SearchBar.options_container.tween('height', '0px').hide();
			SearchBar.options_state = 'closed';
		}
	
	}, 
	switch_search_target : function () {	
		SearchBar.options_display.set('html', this.get('rel'));
		SearchBar.search_target_link.removeClass('orange').addClass('white');
		SearchBar.search_target_link = this;
		this.addClass('orange').removeClass('white');
		SearchBar.search_target = this.get('rel');
		SearchBar.term.focus();
		SearchBar.reload_cats();
		SearchBar.toggle_options();
	},
	
	
	set_auto_close : function () {
		console.log('setting autoclose');
		$('header').addEvent('click', SearchBar.do_auto_close);
		$('content').addEvent('click', SearchBar.do_auto_close);
	},
	
	do_auto_close : function () {
		console.log('fired  autoclose: '+SearchBar.state);
		if (SearchBar.state == 'open')
			SearchBar.toggle_search();
		$('header').removeEvent('click', SearchBar.do_auto_close);
		$('content').removeEvent('click', SearchBar.do_auto_close);
	}


	

}


/* END component/global_search_bar
*************************************************************/

/**************************************************************
 BEGIN component/media_lit_home                                           */
/* Could not locate javascript file: component/media_lit_home*/

/* END component/media_lit_home
*************************************************************/

/**************************************************************
 BEGIN component/page_menu                                           */
/* Could not locate javascript file: component/page_menu*/

/* END component/page_menu
*************************************************************/

/**************************************************************
 BEGIN component/section_menu                                           */
child_menu = {

	init : function (tEle) {
	
		this.trigger = tEle;
		this.timer = false;

		this.child = this.trigger.getParent().getNext();
		this.child.show();				
		
		this.shad = new Element('div').setStyle('backgroundImage', 'url(site/img/semi_trans.png)');
		this.shad.setStyle('position', 'absolute');
		this.shad.setStyle('top', this.child.getPosition().y + 5);
		this.shad.setStyle('left', this.child.getPosition().x + 5);
		this.shad.setStyle('width', this.child.getSize().x);
		this.shad.setStyle('height', this.child.getSize().y);
		this.shad.setStyle('zIndex', '9998');

		
		this.child.addEvent('mouseleave', this.hide_child_menu);
		this.child.addEvent('mouseenter', this.stop_timer);
		
		this.trigger.addEvent('mouseleave', this.start_timer);

		this.shad.inject(document.body);
		return true;
	},
	
	stop_timer : function () {
		if (!this.timer)
			return false;
		
		clearTimeout(this.timer);
		this.timer = false;
		return true;	
	},
	start_timer : function () {
		if (this.timer)
			this.stop_timer();
		this.timer = setTimeout('ChildMenu.hide_child_menu();', 1000);
		return true;
	},
	hide_child_menu : function () {
		if (this.timer)
			this.stop_timer();
		this.shad.destroy();
		this.child.hide();
		return true;
	}
}

/*

The MIT License

BySlideMenu (http://www.byscripts.info/mootools/byslidemenu)
Copyright (c) 2008 ByScripts.info (http://www.byscripts.info)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

var BySlideMenu = new Class({
	Implements: Options,

	options: {
		defaultIndex: false,
		expandMode: 'mouseover',
		pinMode: false,
		vertical: false,
		compressSize: 40,
		elementWidth: 320,
		elementHeight: 240,
		autoSize: true,
		duration: 500,
		transition: 'linear',
		containerWidth: null,
		containerHeight: null,
		useOverflow: false
	},
	
	initialize: function(containerId, options){
		this.setOptions(options);
		this.elementsId = [];
		this.activeId = 0;
		this.containerId = $pick(containerId, 'byslidemenu');
		
		var container = $(this.containerId);	
		
		container.addEvent('mouseleave', function(){
			this.resetAll();
		}.bind(this));
		
		var elements = container.getChildren();
		var num = elements.length;

		var imgHeight = null, imgWidth = null;
		if(this.options.autoSize)
		{
			var firstImg = elements[0].getElement('img');
		
			if(firstImg)
			{
				imgHeight = firstImg.getHeight();
				imgWidth = firstImg.getWidth();
			}
		}
		
		var offsetWidth = 
			elements[0].getStyle('padding-left').toInt()
			+ elements[0].getStyle('padding-right').toInt()
			+ elements[0].getStyle('border-left-width').toInt()
			+ elements[0].getStyle('border-right-width').toInt();
		var offsetHeight = 
			elements[0].getStyle('padding-top').toInt()
			+ elements[0].getStyle('padding-bottom').toInt()
			+ elements[0].getStyle('border-top-width').toInt()
			+ elements[0].getStyle('border-bottom-width').toInt();
		
		if(this.options.vertical)
		{
			this.posAttr = 'top';
			var containerWidth = $pick(imgWidth, this.options.containerWidth, this.options.elementWidth);
			if(containerWidth == "full")
				containerWidth = container.getParent().getStyle('width').toInt();
			if(this.options.containerHeight)
			{
				if(this.options.containerWidth == 'full')
					var containerHeight = container.getParent().getStyle('height').toInt();
				else
					var containerHeight = this.options.containerHeight;

				this.openSize = containerHeight - ((num - 1) * this.options.compressSize);
			}
			else
			{
				this.openSize = $pick(imgHeight, this.options.elementHeight);
				var containerHeight = this.openSize + ((num - 1) * this.options.compressSize);
			}
			
			this.closeSize = containerHeight / num;
			
			var elementHeight = this.openSize;
			var elementWidth = containerWidth;
		}
		else
		{
			this.posAttr = 'left';
			var containerHeight = $pick(imgHeight, this.options.containerHeight, this.options.elementHeight);
			if(containerHeight == "full")
				containerHeight = container.getParent().getStyle('height').toInt();
			if(this.options.containerWidth)
			{
				if(this.options.containerWidth == 'full')
					var containerWidth = container.getParent().getStyle('width').toInt();
				else
					var containerWidth = this.options.containerWidth;

				this.openSize = containerWidth - ((num - 1) * this.options.compressSize);
			}
			else
			{
				this.openSize = $pick(imgWidth, this.options.elementWidth);
				var containerWidth = this.openSize + ((num - 1) * this.options.compressSize);
			}
			this.closeSize = containerWidth / num;
			
			var elementHeight = containerHeight;
			var elementWidth = this.openSize;
		}

		container.setStyles({
			padding: 0,
			position: 'relative',
			overflow: 'hidden',
			width: containerWidth,
			height: containerHeight
		});
		
		var id = 0;
		
		elements.each(function(element){
			var beforePos = id * this.options.compressSize;
			var afterPos = this.openSize + ((id - 1) * this.options.compressSize);
			var closePos = id * this.closeSize;
			element.setStyles({
				position: 'absolute',
				height: elementHeight - offsetHeight,
				width: elementWidth - offsetWidth
			});
			element.setStyle(this.posAttr, closePos);
			element.set('tween', {
				duration: this.options.duration,
				transition: this.options.transition
			});
			
			id++;
			
			element.set('id', this.containerId + '_Elm' + id);
			element.store('id', id);
			
			element.store('beforePos', beforePos);
			element.store('afterPos', afterPos);
			element.store('closePos', closePos);

			this.elementsId.include(id);
			
			if([this.options.pinMode, this.options.expandMode].contains('mouseover'))
			{
				element.addEvent('mouseenter', function(element){
					if(this.options.expandMode == 'mouseover')
						this.expand(element, this.options.pinMode == 'mouseover');
				}.bind(this, element));
			}
			
			if(this.options.pinMode || this.options.expandMode == 'click')
			{
				element.addEvent('click', function(element){
					if(this.options.defaultIndex == element.retrieve('id'))
					{
						//this.options.defaultIndex = 0;
						this.resetAll();
					}
					else if(this.options.expandMode == 'click')
						this.expand(element, this.options.pinMode == 'click');
					else
						this.options.defaultIndex = element.retrieve('id');
				}.bind(this, element));
			}

		}, this);
		
		if(this.options.defaultIndex)
			this.expand(this.options.defaultIndex, false, true);
	},
	
	expand: function(element, setDefault, noAnim){
		if($type(element) == 'number')
			element = $(this.containerId + '_Elm' + element);

		if(this.options.useOverflow)
			this.clearOverflow();
		
		var currentId = element.retrieve('id');
		this.activeId = currentId; 
		
		if(this.options.useOverflow)
			this.switchOverflowTimer = this.switchOverflow.delay(this.options.duration, this, element);
		
		if(setDefault)
			this.options.defaultIndex = currentId;
		
		this.elementsId.each(function(elementId){
			var elm = $(this.containerId + '_Elm' + elementId);
			if(elementId > currentId)
				this.compressAfter(elm, noAnim);
			else
				this.compressBefore(elm, noAnim);
		}, this);
	},
	
	switchOverflow: function(element){
		element.setStyle('overflow', 'auto');
	},
	
	clearOverflow: function(){
		$clear(this.switchOverflowTimer);
		$(this.containerId).getChildren().setStyle('overflow', '');
	},
	
	compressBefore: function(element, noAnim){
		var pos = element.retrieve('beforePos');
		var tween = element.get('tween', {property: this.posAttr, duration: this.options.duration, transition: this.options.transition});
		
		if(noAnim)
			tween.set(pos);
		else
			tween.start(pos);
	},
	
	compressAfter: function(element, noAnim){
		var pos = element.retrieve('afterPos');
		var tween = element.get('tween', {property: this.posAttr, duration: this.options.duration, transition: this.options.transition});
		if(noAnim)
			tween.set(pos);
		else
			tween.start(pos);
	},
	
	reset: function(element){
		var pos = element.retrieve('closePos');
		element.get('tween', {property: this.posAttr, duration: this.options.duration, transition: this.options.transition}).start(pos);
	},
	
	resetAll: function(){
		
		if(this.options.useOverflow)
			this.clearOverflow();

		if(this.options.defaultIndex) {
			this.expand(this.options.defaultIndex);
		} else
		{
			this.elementsId.each(function(elementId){
				this.reset($(this.containerId + '_Elm' + elementId));
			}, this);
		}
	}
});

/* END component/section_menu
*************************************************************/

/**************************************************************
 BEGIN component/account_menu                                           */
account_menu_obj = new Class({
	initialize: function (uid) {
		this.uid = uid;
		this.current_page = $('account_menu_current_page').get('html');
		this.drawBarElements();
		this.showed_cart_popup = false;
	},
	update: function () {
		if (!$('account_menu'))
			return false;
		$('account_menu').set('html', '<img src="'+URL+'site/img/system/small_loading.gif" />' + $('account_menu').get('html'));
		jsonreq('account_menu', 'action=AJAX_refresh_account_menu', this.updateCallback.bind(this));
		return false;
	},
	updateWithCart: function () {
		this.showed_cart_popup = false;
		this.update();
	},
	updateCallback: function (resp) {
		if (resp.err)
			return show_error(resp.msg);
		if (!$('account_menu'))
			return false;
		$('account_menu').set('html', resp.data);
		this.drawBarElements();
		return false;
	},
	drawBarElements: function () {
		if (!$('account_menu'))
			return false;
		this.items_in_cart = $('account_menu_items_in_cart').get('html');
		if (this.items_in_cart > 0 && this.current_page != 'cart' && !this.showed_cart_popup) {
			this.showed_cart_popup = true;
			var cartTip = $('cart_tip');
			cont = this._createCartPopup(cartTip);
			cont.style.left = (cartTip.getPosition().x + 16 ) + 'px';
			setTimeout("$('cart_tipbox').fade('out')", 3000);
			setTimeout("$('cart_tipbox').dispose()", 6000);
		}
	},
	_createCartPopup: function (cart_link) {
		var cont = document.createElement('div');
		cont.id = "cart_tipbox";
		cont.style.position = 'absolute';
		cont.style.top = '26px';				

		var tiptop = document.createElement('div');
		tiptop.className = 'tip-top';
		
		var tip = document.createElement('div');
		tip.className = 'tip';
		
		var tiptitle = new Element('div')
			.set('html', 'You have items in your cart!')
			.addClass('largeText').addClass('bold').addClass('black');
		
		var tiptext = document.createElement('div');
		tiptext.className = 'tip-text';
		tiptext.innerHTML = 'You may continue shopping,<br/>or click "My Cart" to check out now.';
		
		tip.appendChild(tiptitle);
		tip.appendChild(tiptext)
		
		var tipbottom = document.createElement('div');
		tipbottom.className = 'tip-bottom';	
		
		cont.appendChild(tiptop);
		cont.appendChild(tip);
		cont.appendChild(tipbottom);

		cont.style.zIndex = '6000';
		cont.style.visibility = 'visible';
		cont.className = 'cart_tipbox';			
		document.body.appendChild(cont);
		return cont;
	}
});

/* END component/account_menu
*************************************************************/